1. 分布式任务调度
- 在系统需要运行大量耗时定时任务的场景下,单使用类似 Quartz 或者 spring task 等定时任务框架无法满足对并发处理性能、监控管理及运维拓展的要求。
- 此时,定时任务主要面临以下几个新问题:
1、多节点重复执行某一任务;
2、大量的任务管理困难;
3、某些大型任务耗时超长,需要切分给多台机器并行执行。 - 如果仅仅面向解决第一个问题,我们可以借助分布式锁,来规避节点的执行难题。
- 这里介绍两个框架 elastic-job 和 xxl-job ;
区别:
- X-Job 侧重的业务实现的简单和管理的方便,学习成本简单,失败策略和路由策略丰富。推荐使用在“用户基数相对少,服务器数量在一定范围内,任务数量多”的情景下使用
- E-Job 关注的是数据,增加了弹性扩容和数据分片的思路,以便于更大限度的利用分布式服务器的资源。但是学习成本相对高些, 推荐在“数据量庞大,且部署服务器数量较多”时使用。
2. E-Job 分布式调度
- Elastic-Job 是一个分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和Elastic-Job-Cloud 组成。Elastic-Job-Lite 定位为轻量级无中心化解决方案,使用 jar 包的形式提供分布式任务的协调服务;
- 基于 quartz 定时任务框架为基础的,因此具备 quartz 的大部分功能使用 zookeeper 做协调,调度中心,更加轻量级;
- 支持任务的分片;
- 支持弹性扩容,可以水平扩展,当任务再次运行时,会检查当前的服务器数量,重新分片,分片结束之后才会继续执行任务;
- 失效转移,容错处理,当一台调度服务器宕机或者跟 zookeeper 断开连接之后,会立即停止作业,然后再去寻找其他空闲的调度服务器,来运行剩余的任务;
- 提供运维界面,可以管理作业和注册中心;
- E-Job的无中心化方式,非常适合整合普通业务。
- 这里主要介绍 Elastic-Job-Lite 的去中心化解决方案。
2.1 使用场景
- 分布式调度协调
- 弹性扩容缩容
- 失效转移
- 错过执行作业重触发
- 作业分片一致性,保证同一分片在分布式环境中仅一个执行实例
- 自诊断并修复分布式不稳定造成的问题
- 支持并行调度
- 支持作业生命周期操作
- 丰富的作业类型
- Spring 整合以及命名空间提供
- 运维平台
2.2 springboot工程,引入依赖
<dependency>
<groupId>com.github.yinjihuan</groupId>
<artifactId>elastic-job-spring-boot-starter</artifactId>
<version>1.0.2</version>
</dependency>
- 因为这个当当网提供的jar包不在国外的maven仓库内,所以要使用GitHub 开源代码库
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
2.3 配置文件
- 需要 zookeeper 支持
# zk注册中心
elastic.job.zk.serverLists=127.0.0.1:2181
elastic.job.zk.namespace=job_elastic
server.port=8082
2.4 开启E-Job
- @EnableElasticJob
2.5 创建ElasticService
package com.example.elasticjob.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class ElasticService {
public void process(String sql) {
log.info("当前执行sql:{}", sql);
}
}
2.6 创建定时任务监听
package com.example.elasticjob.handle;
import com.dangdang.ddframe.job.executor.ShardingContexts;
import com.dangdang.ddframe.job.lite.api.listener.ElasticJobListener;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MessageElasticJobListener implements ElasticJobListener {
public void beforeJobExecuted(ShardingContexts shardingContexts) {
log.info("{}任务执行前",shardingContexts.getJobName());
}
public void afterJobExecuted(ShardingContexts shardingContexts) {
log.info("{}任务执行后",shardingContexts.getJobName());
}
}
2.7 创建定时任务异常处理类
package com.example.elasticjob.handle;
import com.dangdang.ddframe.job.executor.handler.JobExceptionHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CustomJobExceptionHandler implements JobExceptionHandler {
public void handleException(String s, Throwable throwable) {
log.info("{}发生异常了,给领导发邮件,{}", s, throwable.getMessage());
}
}
2.8 创建定时任务
package com.example.elasticjob.job;
import com.cxytiandi.elasticjob.annotation.ElasticJobConf;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.example.elasticjob.service.ElasticService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@Slf4j
@ElasticJobConf(name = "ElasticSimpleJob", cron = "0/5 * * * * ?",//配置触发时间
//分片序列号和参数用等号分隔,多个键值对用逗号分隔;分片序列号从0开始,不可大于或等于作业分片总数
shardingItemParameters = "0=nanjing,1=shanghai",
shardingTotalCount = 2,//作业分片总数
listener = "com.example.elasticjob.handle.MessageElasticJobListener",//任务开始和结束时,自定义的处理功能
jobExceptionHandler = "com.example.elasticjob.handle.CustomJobExceptionHandler")//任务异常时,自定义的处理
public class ElasticSimpleJob implements SimpleJob {
@Autowired
private ElasticService elasticService;
public void execute(ShardingContext shardingContext) {
log.info("当前执行分片:{}", shardingContext.getShardingParameter());
String sql = shardingContext.getShardingParameter();
elasticService.process(sql);
}
}
2.9 启动zookeeper,再使用8081端口启动服务
- 当只有一个服务时间,会同时执行2个分片的sql
2.10 再使用8082端口启动服务
- ElasticJobApplication类和ElasticJobApplication2类代码一样,只是为了同时启动2个服务,修改配置文件端口号,启动ElasticJobApplication2,可以看出只执行nanjing分片的sql
- 再次查看ElasticJobApplication的打印,当有2个服务时,只会执行shanghai分片的sql
3. X-Job分布式调度
- XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。与 E-Job 走无中心化方式不同,XXL-JOB 是中心集权方式。
3.1 使用场景
- 当项目中定时任务非常多的时候,建议使用X-Job;
- 由job配置中心通过quartz控制客户端job触发时机,然后通过netty rpc调用执行客户端的具体实现。使用中心化的方式可以极大地减少job的管理成本,还可以配置集群。
- xxl-job的官方文档非常详细,这里只介绍一个基本搭建和使用
https://www.xuxueli.com/xxl-job
3.2 项目下载
- GitHub下载https://github.com/xuxueli/xxl-job
- Gitee下载http://gitee.com/xuxueli0323/xxl-job
- 我这里是从GitHub下载的代码,速度有点慢,导入idea后
3.3 初始化数据库
- 在mysql中执行下面的sql脚本,Mysql版本要在5.7之上
3.4 编译源码
- 执行mvn clean compile即可
3.5 配置调度中心(xxl-job-admin)
- 修改配置文件,数据库地址,用户名,密码
3.6 启动调度中心
- 调度中心如果要做集群部署时,由以下要求和建议:
1、DB配置保持一致;
2、集群机器时钟保持一致(单机集群忽视);
3、建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。
3.7 登陆调度中心控制台
- http://localhost:8080/xxl-job-admin,默认"admin/123456"。
3.8 配置执行器项目
- 执行器就是业务系统,来接收“调度中心”的调度执行业务代码。
- 在xxl-job-executor-samples中,作者为我们提供很多工作中常用的框架案例,大家可以把自己工作中的项目按照作者提供的例子进行改造,来使用xxl-job(要保证pom文件中引入了 “xxl-job-core” 的依赖)。如果是新项目也可以直接在作者的例子中进行开发。
- 这里就直接使用作者提供的springboot版本执行器写一个定时任务进行测试。
- 因为是本机测试,这里都是用的默认,什么都没改
3.9 创建测试类
- SampleXxlJob类中,作者提供5中示例,还写了一些注意事项。
- 创建一个测试类MyJob
3.10 启动执行器项目,添加任务
3.11 启动任务
- 每隔1s执行一次
3.12 查看日志
3.13 线上操作
- 可以随时停止或启动定时任务
- 可以修改cron表达式
- 使用glue(java),可以在线添加定时任务