一、#创作灵感#
为了在多数据源的SpringBoot应用中实现ElasticJob3.0.4事件追踪各种捣腾,固写下此笔记以巩固知识要点
二、环境
- SpringBoot 2.7.18 官方下载地址:SpringBoot 2.7.18
- ElasticJob 3.0.4 官方下载地址:ElasticJob 3.0.4
- ElasticJob-UI 3.0.2 图形化工具 官方下载地址:ElasticJob-UI 3.0.2
- Nacos 2.2.3 官方下载地址:Nacos 2.2.3
- Oracle JDK8u202(Oracle JDK8最后一个非商业版本) 下载地址:Oracle JDK8u202
三、POM依赖
<!-- elasticjob依赖 -->
<dependency>
<groupId>org.apache.shardingsphere.elasticjob</groupId>
<artifactId>elasticjob-lite-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
四、数据源配置
我的配置放在nacos中的,格式是properties
四个数据源配置如下:
# @author brick man
# clickhouse 时序数据库
spring.datasource.clickhouse.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.clickhouse.driver-class-name=com.clickhouse.jdbc.ClickHouseDriver
spring.datasource.clickhouse.jdbc-url=jdbc:clickhouse://********:8123/real_saas_health_degree
spring.datasource.clickhouse.username=saas_health
spring.datasource.clickhouse.password=******
# 是允许连接在连接池中空闲的数量
spring.datasource.clickhouse.minimum-idle=5
# 是允许连接在连接池中空闲的最长时间(以毫秒为单位)
spring.datasource.clickhouse.idle-timeout=600000
# 配置最大池大小
spring.datasource.clickhouse.maximum-pool-size=20
# 配置从池返回的连接的默认自动提交行为。默认值为true
spring.datasource.clickhouse.auto-commit=true
# 连接池的名称
spring.datasource.clickhouse.pool-name=MyHikariCP
# 池中连接关闭后的最长生命周期(以毫秒为单位)
spring.datasource.clickhouse.max-lifetime=1800000
# 是客户端等待连接池连接的最大毫秒数
spring.datasource.clickhouse.connection-timeout=120000
# 开启连接监测泄露 本地网络耗时大设长一点15s,生产可设5s
spring.datasource.clickhouse.leak-detection-threshold=15000
# 测试连接数据库
spring.datasource.clickhouse.connection-test-query=select version();
# mysql数据库存AI平台主数据
spring.datasource.mysql-ai.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.mysql-ai.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.mysql-ai.jdbc-url=jdbc:mysql://******:3307/sml
spring.datasource.mysql-ai.username=sml
spring.datasource.mysql-ai.password=******
# pg数据库存业务统计数据
spring.datasource.postgresql-gatherds.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.postgresql-gatherds.driver-class-name=org.postgresql.Driver
spring.datasource.postgresql-gatherds.jdbc-url=jdbc:postgresql://******:18921/lte_hjdb
spring.datasource.postgresql-gatherds.username=crm_qry
spring.datasource.postgresql-gatherds.password=******
# mysql数据库存elasticjob事件追踪数据
spring.datasource.mysql-elasticjob.jdbc-url=jdbc:mysql://******:3307/elasticjob?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
spring.datasource.mysql-elasticjob.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.mysql-elasticjob.username=elasticjob
spring.datasource.mysql-elasticjob.password=******
五、ElasticJob追踪配置
# author brick man
# 持久化类型 RDB: relation database -- 关系型数据库
elasticjob.tracing.type=RDB
# 数据源配置前缀,作者这里集成时不知何故未生效,最终通过把MySQL-elasticjob数据源设置为主数据源(@Primary)才集成成功
elasticjob.tracing.data-source=spring.datasource.clickhouse
到这里所有集成工作就已经完成
特别注意:
1、新版本的elasticjob集成时不需要创建作业配置类(老版本需要)
2、作者这里可能因为springboot版本的差异把elasticjob数据源设置为主数据源才成功(希望读者用的springboot3的最新版本,这样会比我幸运)
3、elasticjob的配置可借助intelJ等IDE的配置联想功能查看有哪些配置(官方配置文档这个配置不太好找、配置说明也不一定完备)
六、指定主数据源(非必须)
上面的步骤如果已经集成成功可跳过这一步
条件:ElasticJob使用了上面指定的数据源配置且应用启动不报错(可跳过这一步)
通过@Primary 注解将数据源标记为主数据源
1、数据源配置类
import com.******.nctc.common.configuration.MySQLElasticJobDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* @Description: MySQL 数据源配置类
* @Author: brick man
* @CreateDate: 2024/12/31
* @Version: 1.0
*/
@Configuration
@MapperScan(basePackages = "com.******.nctc.elasticjob.mapper",
annotationClass = MySQLElasticJobDataSource.class,
sqlSessionFactoryRef = "mysqlElasticJobSessionFactory")
public class MySQLElasticJobDataSourceConfig {
@Bean(name = "mysqlElasticJobDataSource")
@Primary // 将该数据源标记为主数据源
@ConfigurationProperties("spring.datasource.mysql-elasticjob")
public DataSource mysqlElasticJobDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "mysqlElasticJobSessionFactory")
public SqlSessionFactory mysqlElasticJobSqlSessionFactory(@Qualifier("mysqlElasticJobDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setSqlInjector(new DefaultSqlInjector());
sqlSessionFactoryBean.setGlobalConfig(globalConfig);
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
Interceptor[] plugins = {interceptor};
sqlSessionFactoryBean.setPlugins(plugins);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:mapper/elasticjob/*.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCallSettersOnNulls(true);
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean.getObject();
}
@Bean("mysqlElasticJobSqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("mysqlElasticJobSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
}
2、数据源注解类
import java.lang.annotation.*;
/**
* @Target({ElementType.TYPE}) 表示该注解只在类上生效
* @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时有效
* @Documented 该注解会被包含在 Javadoc 中产生的文档中
* @author brick man
* @date 20241231
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MySQLElasticJobDataSource {
}
注意
作者这里调整了原来的主数据源,为了避免工程现在功能出现问题,需要对所有mapper指定数据源
七、elasticjob-3.0.2-ui
1、下载elasticjob-3.0.2-ui
下载:见前面第二节 环境 的下载连接
2、安装elasticjob-3.0.2-ui
选择适合自己的版本后解压--->修改数据,启动即可(需要jdk8+)
作者用的:elasticjob-3.0.2-lite-ui-bin.tar.gz
-(1) 数据库驱动放ext-lib目录
解压后需要将对应数据库驱动放ext-lib目录
作者这里用的mysql存储,所以把mysql驱动包放ext-lib目录
-(2)修改配置
作者的配置示例:
server.port=8089
# web 控制台张米
auth.username=root
auth.password=******
auth.token_expires_after_seconds=3600
spring.datasource.default.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.default.url=jdbc:mysql://localhost:3306/elasticjob?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# 数据库张米
spring.datasource.default.username=elasticjob
spring.datasource.default.password=******
spring.jpa.show-sql=false
## Uncomment the following property to allow adding DataSource dynamically.
dynamic.datasource.allowed-driver-classes={'org.h2.Driver','org.postgresql.Driver','com.mysql.cj.jdbc.Driver'}
-(3) start.sh脚本JVM参数调整
- -Xss参数调整为512k,默认太小,一般启动会报错
-(4) start.sh脚本指定jdk环境变量
- 如果机器上有jdk8+环境变量可跳过
- jdk17会报错因为starts.h脚本中个别jvm参数只有8支持
- 解决覆盖PATH环境变量JDK8环境变量不生效的问题(还是JDK17),如果机器的环境变量是17且采用的是程序全量安装的方式安装(桌面、程序组都有),可参考作者下面的的方式(把java命令替换成jdk8的java脚本的绝对路径)解决
作者修改后的脚本如下:
SERVER_NAME=ShardingSphere-ElasticJob-UI
# 指定JAVA_HOME
export JAVA_HOME=/usr/java/jdk1.8.0_202-amd64
export PATH=/bin:/usr/sbin:/sbin:/usr/local/sbin::$HOME/.local/bin:$JAVA_HOME/bin:/usr/bin
export JAVA_HOME
# 作者上面修改了环境变量(大部分情况下能生效),但服务器上下面这句输出的依然是open jdk17
# 故作者在后面执行java时使用了jdk8的绝对路径
echo `/usr/java/jdk1.8.0_202-amd64/bin/java -version`
cd `dirname $0`
cd ..
DEPLOY_DIR=`pwd`
LOGS_DIR=${DEPLOY_DIR}/logs
if [ ! -d ${LOGS_DIR} ]; then
mkdir ${LOGS_DIR}
fi
STDOUT_FILE=${LOGS_DIR}/stdout.log
PIDS=`ps -ef | grep java | grep "$DEPLOY_DIR" | grep -v grep | awk '{print $2}'`
if [ -n "$PIDS" ]; then
echo "ERROR: The $SERVER_NAME already started!"
echo "PID: $PIDS"
exit 1
fi
CLASS_PATH=.:${DEPLOY_DIR}/conf:${DEPLOY_DIR}/lib/*:${DEPLOY_DIR}/ext-lib/*
JAVA_OPTS=" -server -Xmx1g -Xms1g -Xmn512m -Xss512k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
MAIN_CLASS=org.apache.shardingsphere.elasticjob.lite.ui.Bootstrap
echo "Starting the $SERVER_NAME ..."
# 使用了jdk8的绝对路径解决环境变量问题
nohup /usr/java/jdk1.8.0_202-amd64/bin/java ${JAVA_OPTS} -classpath ${CLASS_PATH} ${MAIN_CLASS} > ${STDOUT_FILE} 2>&1 &
sleep 1
echo "Please check the STDOUT file: $STDOUT_FILE"
3、启动elasticjob-3.0.2-ui
${ElasticjobUI_HOME}/bin/start.sh
4、登陆elasticjob-3.0.2-ui
5、添加注册中心配置
6、查看所有作业
7、作业执行事件追踪
- 历史轨迹
可查看每个作业每次执行的起止时间(从而可计算出耗时)
- 查询耗时超过1分钟的Job名及次数
通过该类查询看是否有耗时超过预期的作业,从而针对性的分析优化
-- 查询耗时超过1分钟的Job名及次数
select job_name,count(0)
from JOB_EXECUTION_LOG
where complete_time-start_time>60
group by job_name
八、知识点总结
1、elasticjob数据源的指定(不指定默认使用主数据源)
2、elasticjob不同的版本使用各不相同(一般较新版本使用会更简单,配置更少)
3、elasticjob的配置可借助intelJ等IDE的配置联想功能查看有哪些配置(官方配置文档并不发找、配置说明也不一定完备)
4、java环境变量问题可通过shell脚本中java命令用jdk8的绝对路径代替
九、其他相关重要知识点
1、任务分继承SimpleJob类的任务、继承DataflowJob的任务
2、elasticjob任务分片后同一个实例上运行的多个片是并行执行的。