介绍
在分布式中,很多微服务可能存在多实例部署的现象,如果在某个具体的微服务中实现一个定时任务,而该微服务存在多个实例的话,那么会导致该定时任务在不同实例中都会进行执行!这很容易导致脏数据、数据重复等问题,因此在通过微服务实现定时任务时并不能像spring boot那样简单。具体实现思路存在两种:
- 分布式锁,一旦存在实例中的一个定时任务执行,则抢占该分布式锁,其他实例的定时任务当抢占不到该锁时则不进行后续执行;
- 调动中心:采用调度中心调度定时任务的执行,可以方便的实现修改调度策略,实现功能上的解耦。XXL-JOB就是这种调度中心;
XXL-JOB的官网学习地址:https://www.xuxueli.com/xxl-job/#《分布式任务调度平台XXL-JOB》
在XXL-JOB中存在两个组件:调度中心和执行器;想要明白什么时调度中心和执行器首先需要简单逻辑XXL-JOB的定时任务执行策略,为了应对分布式定时任务执行,XXL-JOB将任务调度逻辑单独抽取出来实现了一个调度中心,调度中心只负责任务的调度,任务的具体执行是由执行器进行负责的,也就是说:调度中心根据任务执行策略,在满足任务执行的条件下调度某个实例或者所有实例进行执行,当满足执行条件时,调度中心将发送给执行器一条消息,告诉执行器需要进行执行某项作业。
调度中心:一个spring boot项目,可以维护任务执行策略以及一部分任务执行内容,例如可以将执行具体内容维护到调度中心【更新数据的源码】,虽然调度中心可以维护执行源码,但具体的执行过程中并不是调度中心进行,调度中心仅仅负责按照执行策略将命令信息发送给执行器;
执行器:可以是一个单独的项目也可以集成到业务项目中,任务的具体执行单位,接收到调度中心的消息后,会将消息进行解析并执行,如果调度中心的消息是一段源码,则执行器将对源码进行编译并进行执行。
通过上述描述,不难看出调度中心和执行器的分工不同,这种设计的模式可以将执行过程和调度过程有效分离,避免不同执行器对调度中心的干扰,有效实现调动中心的解耦。
调度中心部署
根据介绍内容可知,xxl-job的部署可以分为调度中心的部署和执行器的部署两部分进行。由于调度中心是一个单独的spring boot项目,因此我们已将其进行单独部署到服务器中,本文中不说明调度中心的集群部署方式,仅针对单体部署进行简单说明。在这里我采用docker部署,具体命令如下:
/**
* 如需自定义 mysql 等配置,可通过 "-e PARAMS" 指定,参数格式 PARAMS="--key=value --key2=value2" ;
* 配置项参考文件:/xxl-job/xxl-job-admin/src/main/resources/application.properties
* 如需自定义 JVM内存参数 等配置,可通过 "-e JAVA_OPTS" 指定,参数格式 JAVA_OPTS="-Xmx512m" ;
*/
docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai" -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin -d xuxueli/xxl-job-admin:{指定版本}
该命令部署时需要生命mysql连接信息、mysql用户名、密码,以及xxl.job.accessToken属性,该属性的值在后续执行器的实现过程中需要被使用到。
上述命令采用了docker部署,调度中心是一个单独的spring boot项目,为了持久化用户配置,该项目使用到了mysql进行配置信息存储,因此,在部署调度中心的过程中需要配置mysql的连接信息,上述是官网提供的部署方案,为了更加灵活的实现配置自定义,还可以将xxl-job中的配置文件:/xxl-job/xxl-job-admin/src/main/resources/application.properties 进行挂载出来,然后配置修改自己的相关配置。执行命令如下:
docker run -p 8080:8080 -e PARAMS="--spring.config.location=/application.properties" -v /tmp:/data/applogs [自己的application.properties配置文件地址]:/APPLICATION.PROPERTIES --name xxl-job-admin -d xuxueli/xxl-job-admin:{指定版本}
通过上述配置完成调度中心容器启动后,可以通过http://localhost:8081/xxl-job-admin/toLogin进行调度中心登录,默认登录账号 “admin/123456”。运行界面如下
至此,调度中心完成配置,刚才我们说到,调度中心需要自己的mysql数据库,如果调度中心无法运行,则我们可以尝试创建一个数据库,相关信息需要和配置文件保持一直。
执行器
执行器可以集成到我们的业务项目中,也可以单独的部署成为一个微服务,在这里我采用单独部署微服务的方式进行实现:在执行器微服务的实现过程中我们首先需要创建一个分布式微服务项目,因为后续执行器的具体执行可以会依赖于spring boot、spring clound、openfeign等架构。在构建完成微服务后,引入以下依赖:
<!-- xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
配置xxl-job执行器
/**
* xxl-job 执行器配置
*/
@Configuration
public class XxlJobConfig {
// 调度中心的地址
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
// 自定义执行器的名称
@Value("${xxl.job.executor.appname}")
private String appName;
// 日志输入地址
@Value("${xxl.job.executor.logpath}")
private String logPath;
// 日志留存时间
@Value("${xxl.job.executor.logretentiondays}")
private int logretentiondays;
// 格外注意:授权token需要和调度中心配置文件中设置的相同
@Value("${xxl.job.accessToken}")
private String accessToken;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appName);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logretentiondays);
return xxlJobSpringExecutor;
}
}
在启动执行器之前,我们还需要再调度中心中配置执行器,否则执行器不能正常注册到调度中心中,配置界面如下:
配置信息完成后,启动执行器,上线机器地址即可查看到我们刚刚启动的执行器。
任务的配置
在定时任务的实现过程中我们可以按照如下流程进行:执行器中实现业务逻辑代码,可以通过openfeign进行给个微服务的调用;调度中心中在任务中心管理界面中新增任务,具体流程如下:
- 执行器实现业务代码,并把入口定义为一个service的方法,该service需要通过@Service注解交由bean容器执行
- 调度中心任务管理界面新增任务,任务配置运行模式选择java,如下
- 保存后通过ide编写执行代码:
- 在代码的编写过程中我们可以使用@Autowired实现bean容器中的bean的依赖注入;建议首先使用idea编译器实现一版后辅助到调度中心中。如果运行的任务需要进行入参配置,还可以配置方法的参数等相关信息。
总结
本文主要简单描述了xxl-job从搭建到使用的基本过程,很多细节及原理问题并未进行深究,设计内容及主要注意事项有一些方面:
- 调度中心和执行器是分开部署的,调度中心和执行器需要使用相同的accessToken否则执行器无法注册到调度中心中
- 调度中心部署的过程中需要生命数据库相关信息
- 不同执行器微服务的appname不能相同
- 在调度中心进行配置执行器代码的过程中可以使用到依赖注入等spring 特性
- 执行器的具体执行工作可以使用自身业务逻辑+openfeign的形式进行实现