什么是任务调度

什么是任务调度?某一时间段进行任务的操作。

具体任务调度有哪些应用的场景?数据同步、交易信息、清除用户的信息、定期发送报表数据、活动推送等。

传统实现定时任务的方式?Thread、TimeTask、ScheduleExecutorService、Quartz 等;不过,这几种方式都是在单点系统使用,一旦Job服务器宕机之后,就必须采取一些措施;具体操作如下:

(1) 使用心跳检测监控自动重启、任务补偿机制(任务做标记)

(2) 定时任务在执行代码的时候中间突然报错,使用日志记录错误,跳过继续执行,在使用定时Job 扫描日志错误记录,进行补偿信息。

(3) 定时Job 在执行的时候,导致整个 Job 异常结束掉,发送邮件通知给运维人员。

分布式定时任务的方式?XXL-Job、Elastic-job等。不过,既然采用分布式,那么肯定会遇到项目部署集群,导致任务重复执行多次;具体操作如下:

(1) Zookeeper 实现分布式锁,每次保证拿到锁再执行,效率比较低。

(2) 配置文件中加入定时任务的开关,但是只能保证一台服务器执行,变为单击服务器。

(3) 启动的时候使用数据库唯一标识;同样是效率低。

(4) 分布式调度任务平台,解决了任务幂等问题,Job 负载均衡轮询机制(推荐)。

那么现在我们来总结下,首先传统的定时任务,几乎无法做到高可用,再加上项目部署集群,会导致任务幂等性问题;此时分布式定时任务调度平台便发挥了作用,咱们拿 XXL-Job 来进行说明;相关作用如下:

(1) 支持Job集群,Job 负载均衡轮询机制保证幂等性问题。

(2) 支持Job补偿,如果Job执行失败的话,会自动实现重试机制,超过重启次数后,会发送邮件通知运维人员。

(3) 支持Job日志记录。

(4) 动态配置定时规则,传统定时Job触发规则都是写死在代码中。

XXL-JOB简介

开源社区:https://www.xuxueli.com/xxl-job/

环境:

Maven3+
Jdk1.8+
Mysql5.7+

xxl-job执行原理

调度平台、执行器、任务管理,相关解释如下:

  • 调度平台:统一管理任务调度的平台,负责转发任务到对应的执行服务器。
  • 执行器:定时Job实际执行的服务器地址。
  • 任务管理:执行服务器配置定时任务规则、路由策略、允许模式等。

xxl-job介绍_集群部署

任务Hanlder究竟该如何编写:

  • 继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
  • 注册到Spring容器中:添加"@Component"注解,被Spring容器扫描为Bean实例。
  • 注册到执行器工厂:添加"@JobHandler(value=“自定义jobhandler名称”)“注解;value对应的值是调度中心新建任务的JobHandler属性的值

xxl-job介绍_spring_02

说明:

(1) 其实这里面使用”@Jobhandler"注解是方便将value值与任务执行类对应好,以便在进行任务调度的时候可以找到对应的任务执行类,然后执行方法;

(2) 继承"IJobHandler",然后实现execute方法,这里面利用反射的思想,只要是继承了IJobHandler这个类,就自动执行execute方法。

高可用设计

所谓高可用设计,指的是执行器服务器和调度中心服务器。前者利用分布式调度中心便可以解决同一套代码在不同的执行器服务器中执行不会出现任务重复消息的问题;但是咱们也需要考虑调度中心服务器是否高可用,如下图:

xxl-job介绍_集群部署_03

 所以此时需要利用Nginx的负载均衡的特性,保证调度平台高可用,相关设计如下:

xxl-job介绍_定时任务_04

快速入门

源码下载:http://gitee.com/xuxueli0323/xxl-job

初始化调度数据库

下载项目源码并解压,获取 “调度数据库初始化SQL脚本” 并执行即可。

调度数据库初始化SQL脚本:/xxl-job/doc/db/tables_xxl_job.sql

调度中心支持集群部署,集群情况下各节点务必连接同一个mysql实例;

如果mysql做主从,调度中心集群节点务必强制走主库;

编译源码

解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下:

xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)
    :xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;
    :xxl-job-executor-sample-frameless:无框架版本;

配置部署调度中心

调度中心项目:xxl-job-admin
作用:统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台。

调度中心配置

调度中心配置文件地址:/xxl-job/xxl-job-admin/src/main/resources/application.properties

调度中心配置内容说明:

### 调度中心JDBC链接:链接地址请保持和 2.1章节 所创建的调度数据库的地址一致
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
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=
### 调度中心国际化配置 [必填]: 默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
xxl.job.i18n=zh_CN
## 调度线程池最大线程配置【必填】
xxl.job.triggerpool.fast.max=200
xxl.job.triggerpool.slow.max=100
### 调度中心日志表数据保存天数 [必填]:过期日志自动清理;限制大于等于7时生效,否则, 如-1,关闭自动清理功能;
xxl.job.logretentiondays=30

部署项目

如果已经正确进行上述配置,可将项目编译打包部署。

调度中心访问地址:http://localhost:8080/xxl-job-admin (该地址执行器将会使用到,作为回调地址)

默认登录账号 “admin/123456”, 登录后运行界面如下图所示。

xxl-job介绍_任务调度_05

调度中心集群 

调度中心支持集群部署,提升调度系统容灾和可用性。

调度中心集群部署时,几点要求和建议:

  • DB配置保持一致;
  • 集群机器时钟保持一致(单机集群忽视);
  • 建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。

配置部署执行器

执行器项目:xxl-job-executor-sample-springboot (提供多种版本执行器供选择,现以 springboot 版本为例,可直接使用,也可以参考其并将现有项目改造成执行器)
作用:负责接收“调度中心”的调度并执行;可直接部署执行器,也可以将执行器集成到现有业务项目中。

1、maven依赖

确认pom文件中引入了 “xxl-job-core” 的maven依赖;

2、执行器配置

执行器配置,配置文件地址:/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

执行器配置,配置内容说明:

### 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### 执行器通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=xxl-job-executor-sample
### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
xxl.job.executor.address=
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=9999
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
xxl.job.executor.logretentiondays=30

3、执行器组件配置

执行器组件,配置文件地址:/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/core/config/XxlJobConfig.java

执行器组件,配置内容说明:

@Bean
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;
}

4、部署执行器项目

如果已经正确进行上述配置,可将执行器项目编译打部署,系统提供多种执行器Sample示例项目,选择其中一个即可,各自的部署方式如下。

xxl-job-executor-sample-springboot:项目编译打包成springboot类型的可执行JAR包,命令启动即可;
xxl-job-executor-sample-frameless:项目编译打包成JAR包,命令启动即可;

至此“执行器”项目已经部署结束。

5、执行器集群

执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。

执行器集群部署时,几点要求和建议:

  • 执行器回调地址(xxl.job.admin.addresses)需要保持一致;执行器根据该配置进行执行器自动注册等操作。
  • 同一个执行器集群内AppName(xxl.job.executor.appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。

任务配置

前提:请确认“调度中心”和“执行器”项目已经成功部署并启动;

1、路由策略

当执行器集群部署时,提供丰富的路由策略,包括;

  • FIRST(第一个):固定选择第一个机器;
  • LAST(最后一个):固定选择最后一个机器;
  • ROUND(轮询):;
  • RANDOM(随机):随机选择在线的机器;
  • CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。
  • LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;
  • LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;
  • FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;
  • BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;
  • SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;

2、子任务

每个任务都拥有一个唯一的任务ID(任务ID可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务ID所对应的任务的一次主动调度。

3、调度过期策略

  • 忽略:调度过期后,忽略过期的任务,从当前时间开始重新计算下次触发时间;
  • 立即执行一次:调度过期后,立即执行一次,并从当前时间开始重新计算下次触发时间;

4、阻塞处理策略

调度过于密集执行器来不及处理时的处理策略;

  • 单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
  • 丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
  • 覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;

5、任务超时时间

支持自定义任务超时时间,任务运行超时将会主动中断任务;

6、失败重试次数

支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;