xxl-job分布式任务调度平台的使用
致谢@XXL-JOB
xxl-job项目相关信息
前期准备
框架搭建
项目下载
初始化 “调度数据库”
数据库生成脚本位置:/xxl-job/doc/db/tables_xxl_job.sql
项目部署(此处采用的的部署方式为官方文档中的 2.4小结 提供的方法)
- 将项目中的 core 与 admin 作为单独的任务调度中心部署
- 构造独立的任务执行器
内核为官方的 xxl-job-executor-sample-springboot
环境配置
调度中心配置
配置文件地址:
xxl-job-admin 中的 application.properties
配置内容:
### 调度中心访问地址,若端口号被占用,可在此处修改,此配置下的访问地址为:http://localhost:8080/xxl-job-admin
server.port=8080
server.context-path=/xxl-job-admin
### 调度中心JDBC链接:链接地址请保持和 2.1章节 所创建的调度数据库的地址一致
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
### 报警邮箱
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=xxx@qq.com
spring.mail.password=xxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
### 调度中心通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### 调度中心国际化配置 [选填]: 默认为空,表示中文; "en" 表示英文;
xxl.job.i18n=
## 调度线程池最大线程配置【必填】
xxl.job.triggerpool.fast.max=200
xxl.job.triggerpool.slow.max=100
### 调度中心日志表数据保存天数 [必填]:过期日志自动清理;限制大于等于7时生效,否则, 如-1,关闭自动清理功能;
xxl.job.logretentiondays=30
任务执行器配置
添加maven依赖:
<!-- xxl-job-core -->
<!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-job-core/ -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${最新稳定版本}</version>
</dependency
执行器配置地址:
定时任务模块 hirop-service-job(即官方的 xxl-job-executor-sample-springboot )中的 application.properties
执行器配置内容:
xxl:
job:
admin:
addresses: http://127.0.0.1:8080/xxl-job-admin
executor:
appname: xxl-job-executor-sample
ip:
port: 9999
logpath: /data/applogs/xxl-job/jobhandler
logretentiondays: 30
accessToken:
配置内容详解(与上方配置内容相同):
### 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=xxl-job-executor-sample
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=9999
### 执行器通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
xxl.job.executor.logretentiondays=30
执行器组件配置类:
组件配置类存放路径:job.config 之下的 XxlJobConfig.java
执行器类存放路径:com.xxx.job.handler
@Configuration
@ComponentScan(basePackages = "com.xxx.job.handler")
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
//配置项注入
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.appname}")
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
//执行器组件配置
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppName(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
定时任务实现
任务执行器(采用GLUE模式-Java)
执行器类存放路径:com.xxx.job.handler
@JobHandler(value = "demoJobHandler")
@Service
public class DemJobHandler extends IJobHandler {
private static Logger logger = LoggerFactory.getLogger(DemJobHandler.class);
@Override
public ReturnT<String> execute(String param) throws Exception {
LOGGER.info("XXL-JOB, Hello World");
}
}
调度中心注册
调度中心访问地址:http://localhost:8080/xxl-job-admin(在调度中心application.properties中配置)
创建新的任务:(JobHandler选项要与任务执行器的@JobHandler注解中的值相同)
扩展知识
Cron表达式
网上有很多 cron表达式 的在线生成工具,如:@Json工具网,同时对其表示感谢
多数据源
数据源配置内容:
配置地址:执行器服务中的 application.properties
数据库连接池:采用阿里的Druid,需要把 Druid 相关的依赖添加到 pom 文件中
datasource:
oracle:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@localhost:1521:helowin
username: admin
password: 1234
initialSize: 5
minIdle: 5
maxActive: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 30000
validationQuery: "select 'x' FROM DUAL"
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,slf4j
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
aop:
proxy-target-class: true
mysql:
……
MyBatis-Plus配置类
配置类地址:job.config 中的 MybatisPlusConfig.java
@Configuration
@MapperScan({"com.xxx.job.mapper*","com.xxx.job.mapper.portal"})
public class MybatisPlusConfig implements TransactionManagementConfigurer {
//ORACLE 数据源
@Resource
private DataSource oracleDataSource;
//Oracle数据源
@Bean(name = "oracleDataSource")
@ConfigurationProperties(prefix = "datasource.oracle")
public DataSource oracleDataSource() {
DruidDataSource datasource = new DruidDataSource();
// 设置数据源允许执行多条语句
WallConfig wallConfig = new WallConfig();
wallConfig.setMultiStatementAllow(true);
WallFilter wallFilter = new WallFilter();
wallFilter.setConfig(wallConfig);
datasource.getProxyFilters().add(wallFilter);
return datasource;
}
//MySQL数据源
@Bean(name = "mysqlDataSource")
@ConfigurationProperties(prefix = "datasource.mysql")
public DataSource mysqlDataSource() {
DruidDataSource datasource = new DruidDataSource();
// 设置数据源允许执行多条语句
WallConfig wallConfig = new WallConfig();
wallConfig.setMultiStatementAllow(true);
WallFilter wallFilter = new WallFilter();
wallFilter.setConfig(wallConfig);
datasource.getProxyFilters().add(wallFilter);
return datasource;
}
/**
* 配置动态数据源
* @return 动态数据源
*/
@Primary
@Bean(name = "dynamicDataSource")
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(mysqlDataSource);
// 配置多数据源,依次加入其他数据源到map中
Map<Object, Object> dsMap = new HashMap<Object, Object>();
dsMap.put("oracleDataSource", oracleDataSource);
dsMap.put("mysqlDataSource", oracleDataSource);
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
/**
* mybatis-plus SQL执行效率插件【生产环境可以关闭】
* @Bean
**/
public PerformanceInterceptor performanceInterceptor() {
return new PerformanceInterceptor();
}
/**
* 注入主键生成器
@Bean
public IKeyGenerator keyGenerator(){
return new H2KeyGenerator();
}*/
/****
* 乐观锁
* 取出记录时,获取当前version
* 更新时,带上这个version
* 执行更新时, set version = yourVersion+1 where version = yourVersion
* 如果version不对,就更新失败
* @return
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}*/
/**
* 注入sql注入器
*/
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
@Bean
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new DataSourceTransactionManager(this.dynamicDataSource());
}
}
添加数据源常量
常量添加地址:common模块下新建常量配置包 constants,在其中新建 DataSourceConstants.java
便于使用和后期维护
public class DataSourceConstants {
public static String MYSQL_DATASOURCE_NAME = "mysqlDataSource";
public static String ORACLE_DATASOURCE_NAME = "oracleDataSource";
}
建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称
位置:common模块下
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源名
*
* @param dataSourceName 数据源
*/
public static void setDataSource(String dataSourceName) {
CONTEXT_HOLDER.set(dataSourceName);
}
/**
* 获取数据源名
*
* @return 数据源名
*/
public static String getDataSource() {
return (CONTEXT_HOLDER.get());
}
/**
* 清除数据源名
*/
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
使用
场景:上文中的定时任务执行器类 @JobHandler 中执行对数据库的操作时
使用代码
DataSourceContextHolder.setDataSource(DataSourceConstants.CMP_DATASOURCE_NAME);