spring传统的定时任务@Scheduled,但是这样存在这一些问题 :
-
做集群任务的重复执行问题
-
cron表达式定义在代码之中,修改不方便
-
定时任务失败了,无法重试也没有统计
-
如果任务量过大,不能有效的分片执行
解决这些问题的方案为:xxl-job 分布式任务调度框架
在分布式架构下,一个服务往往会部署多个实例来运行我们的业务,如果在这种分布式系统环境下运行任务调度,我们称之为分布式任务调度
分布式任务调度面临的问题
当任务调度以集群方式部署,同一个任务调度可能会执行多次 所以我们需要控制相同的任务在多个运行实例上只执行一次。常见解决方案
1、分布式锁,多个实例在任务执行前首先需要获取锁,如果获取失败那么就证明有其他服务已经在运行,如果获取成功那么证明没有服务在运行定时任务,那么就可以执行。
2、ZooKeeper选举,利用ZooKeeper对Leader实例执行定时任务,执行定时任务的时候判断自己是否是Leader,如果不是则不执行,如果是则执行业务逻辑,这样也能达到目的
针对分布式任务调度的需求,市场上出现了很多的产品
1) TBSchedule:淘宝推出的一款非常优秀的高性能分布式调度框架,目前被应用于阿里、京东、支付宝、国美等很多互联网企业的流程调度系统中。但是已经多年未更新,文档缺失严重,缺少维护。
2) XXL-Job:大众点评的分布式任务调度平台,是一个轻量级分布式任务调度平台, 其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。
3)Elastic-job:当当网借鉴TBSchedule并基于quartz 二次开发的弹性分布式任务调度系统,功能丰富强大,采用zookeeper实现分布式协调,具有任务高可用以及分片功能。
4)Saturn: 唯品会开源的一个分布式任务调度平台,基于Elastic-job,可以全域统一配置,统一监 控,具有任务高可用以及分片功能。
XXL-Job-环境搭建 docker安装
-
Maven3+
-
Jdk1.8+
-
Mysql5.7+
1.创建mysql容器,初始化xxl-job的SQL脚本
docker run -p 3306:3306 --name mysql57 \
-v /opt/mysql/conf:/etc/mysql \
-v /opt/mysql/logs:/var/log/mysql \
-v /opt/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
2.拉取镜像
docker pull xuxueli/xxl-job-admin:2.3.0
3.创建容器
docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://192.168.200.130:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
--spring.datasource.username=root \
--spring.datasource.password=root" \
-p 8888:8080 -v /tmp:/data/applogs \
--name xxl-job-admin --restart=always -d xuxueli/xxl-job-admin:2.3.0
4、在需要使用到xxl-job的模块中导入依赖
<!--xxl-job-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version>
</dependency>
5、在对应模块中导入xxl-job的相关配置
xxl:
job:
admin:
addresses: http://192.168.200.130:8888/xxl-job-admin
executor:
appname: xxl-job-executor-sample #执行器的名称
port: 9999 #执行器的端口号 最大就是9999
6、在对应模块新建配置类
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
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.port}")
private int port;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setPort(port);
return xxlJobSpringExecutor;
}
}
7、执行定时任务代码
@Component
public class HelloJob {
@XxlJob("demoJobHandler")
public void helloJob(){
System.out.println("简单任务执行了。。。。");
}
}
重要注解:@XxlJob(“demoJobHandler”) XxlJob注解中的内容和新建执行器时有关系
路由策略中轮询和分片广播的区别
轮询:假设有一个每隔一秒定时发布的任务 现在有两个集群模块 第一秒A集群接收任务 第二秒B集群接收,第三秒A集群才会再次接收
分片广播:他是采用任务取模的方式来分配任务的 假设有三个实例也就是0-2三个分片,第一个任务 通过1%3取模因为有三个分片,取模为1所以任务1交给分片1处理 后面以此类推
分片广播适用那种在同一时间有大量定时任务需要处理的场景