⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/u014494148/article/details/121382211 「墨家巨子@俏如来」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一.定时任务概述

1.定时任务认识

1.1.什么是定时任务

定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行,或者周期性的去执行某个任务,比如:每天晚上24点做数据汇总,定时发送短信等。

1.2.常见定时任务方案

  • While + Sleep : 通过循环加休眠的方式定时执行
  • Timer和TimerTask实现 :JDK自带的定时任务,可以实现简单的间隔执行任务(在指定时间点执行某一任务,也能定时的周期性执行),无法实现按日历去调度执行任务。
  • ScheduledExecutorService : Java并发包下,JDK1.5出现,是比较理想的定时任务实现方案。Eureka就使用的是它
  • QuartZ : 使用Quartz,它是一个异步任务调度框架,功能丰富,可以实现按日历调度,支持持久化。
  • 使用Spring Task,Spring 3.0后提供Spring Task实现任务调度,支持按日历调度,相比Quartz功能稍简单,但是在开发基本够用,支持注解编程方式。
  • SpringBoot中的Schedule : 通过@EnableScheduling+@Scheduled最实现定时任务,底层使用的是Spring Task

2.分布式定时任务

2.1.遇到什么问题

上述的定时任务都是集中式(单体项目使用)的定时任务,在分布式中将会面临一些问题或不足

  • 业务量大,单机性能瓶颈需要扩展
  • 多台机器部署如何保证定时任务不重复执行
  • 定时任务时间需要可调整,可以暂停
  • 机器发生故障down机,定时任务依然可用,如何实现故障转移
  • 定时任务,执行日志是否可监控

2.2.分布式定时任务xxl-job

XXL-JOB是一个分布式任务调度平台,于2015问世,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。其具备且不止如下能力

  1. 简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;
  2. 动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效;
  3. 调度中心HA(中心式):调度采用中心式设计,“调度中心”基于集群Quartz实现并支持集群部署,可保证调度中心HA;执行器HA(分布式):任务分布式执行,任务"执行器"支持集群部署,可保证任务执行HA;
  4. 弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;
  5. 路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
  6. 故障转移:任务路由策略选择"故障转移"情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。
  7. 任务失败告警:默认提供邮件方式失败告警,同时预留扩展接口,可方面的扩展短信、钉钉等告警方式;

具体见:https://github.com/xuxueli/xxl-job/tree/v2.0.0

二.XXL-JOB初体验

1.xxl-job架构设计

1.1.设计思想

将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“调度中心”负责发起调度请求。

将任务抽象成分散的JobHandler,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑。因此,“调度”和“任务”两部分可以相互解耦,提高系统整体稳定性和扩展性;

1.2.架构设计图

xxl-job分为 调度中心和执行器两大模块

  • 调度模块(调度中心)

负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;

支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover(故障转移)。

  • 执行模块(执行器)

负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;

接收“调度中心”的执行请求、终止请求和日志请求等。

  • 调度中心高可用

基于数据库的集群方案,数据库选用Mysql;集群分布式并发环境中进行定时任务调度时,会在各个节点会上报任务,存到数据库中,执行时会从数据库中取出触发器来执行,如果触发器的名称和执行时间相同,则只有一个节点去执行此任务。

  • 并行调度

调度采用线程池方式实现,避免单线程因阻塞而引起任务调度延迟。XXL-JOB调度模块默认采用并行机制,在多线程调度的情况下,调度模块被阻塞的几率很低,大大提高了调度系统的承载量。

XXL-JOB的不同任务之间并行调度、并行执行。XXL-JOB的单个任务,针对多个执行器是并行运行的,针对单个执行器是串行执行的。同时支持任务终止。

  • 执行器(任务)高可用

执行器如若集群部署,调度中心将会感知到在线的所有执行器,如“127.0.0.1:9997, 127.0.0.1:9998, 127.0.0.1:9999”。多个执行器可以选择“路由策略”来采用轮询,随机等方式进行多机器调度。

当任务”路由策略”选择”故障转移(FAILOVER)”时,当调度中心每次发起调度请求时,会按照顺序对执行器发出心跳检测请求,第一个检测为存活状态的执行器将会被选定并发送调度请求。调度成功后,可在日志监控界面查看“调度备注”

2.xxl-job安装

2.1.下载源码

请下载项目源码并解压,使用IDEA工具导入项目

源码仓库地址

  • https://github.com/xuxueli/xxl-job
  • http://gitee.com/xuxueli0323/xxl-job

项目代码结构如下

  • doc :文档,即SQL脚本所在目录
  • db : “调度数据库”建表脚本
  • xxl-job-admin : 调度中心项目源码
  • xxl-job-core : 核心模块,公共Jar依赖
  • xxl-job-executor-samples : 执行器,Sample示例项目(大家可以在该项目上进行开发,也可以将现有项目改造生成执行器项目)

2.2.导入数据库

打开项目代码,获取 “调度数据库初始化SQL脚本” 并执行即可。“调度数据库初始化SQL脚本” 位置为: /xxl-job/doc/db/tables_xxl_job.sql ,数据库名:xxl_job

数据库如下

  • xxl_job_lock:任务调度锁表;
  • xxl_job_group:执行器信息表,维护任务执行器信息;
  • xxl_job_info:调度扩展信息表:用于保存XXL-JOB调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;
  • xxl_job_log:调度日志表:用于保存XXL-JOB任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;
  • xxl_job_log_report:调度日志报表:用户存储XXL-JOB任务调度日志的报表,调度中心报表功能页面会用到;
  • xxl_job_logglue:任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能;
  • xxl_job_registry:执行器注册表,维护在线的执行器和调度中心机器地址信息;
  • xxl_job_user:系统用户表;

2.3.启动调度中心

打开 xxl-job-admin 的配置文件,/xxl-job/xxl-job-admin/src/main/resources/application.properties 对调度中心进行配置,重要配置如下

  • server.port : 根据情况修改端口
  • spring.datasource.url :指向刚才准备的数据库
  • spring.datasource.password : 记得修改成自己的数据库密码
  • spring.mail.username :配置自己的邮件账号
  • spring.mail.password :邮件的授权码,我下面是以qq邮箱为例

下面根据自己的情况进行修改,不要直接复制

## 调度中心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=邮箱授权码,不是登录密码
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

然后启动调度中心 ,执行 XxlJobAdminApplication#main 方法 , 启动之后,浏览器访问 http://localhost:18080/xxl-job-admin/jobinfo?jobGroup=2 ;注意URL中有个上下文路径。默认登录账号 “admin/123456”, 登录后运行界面如下图所示。

2.3.配置部署“执行器项目

“执行器”项目:xxl-job-executor-sample-springboot(提供多种版本执行器供选择,现以 springboot 版本为例,可直接使用,也可以参考其并将现有项目改造成执行器)

作用:负责接收“调度中心”的调度并执行;可直接部署执行器,也可以将执行器集成到现有业务项目中。

修改配置:/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

  • xxl.job.admin.addresses : 调度中心的地址,如果调度中心修改过端口,这里也要对应修改

## 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://127.0.0.1:18080/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

上面配置是为了在Spring容器中创建一个 XxlJobSpringExecutor 执行器Bean,见:com.xxl.job.executor.core.config.XxlJobConfig#xxlJobExecutor

@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

return xxlJobSpringExecutor;
}

com.xxl.job.executor.service.jobhandler.SampleXxlJob中提供了简单的定时任务实例

为方便用户参考与快速实用,示例执行器内原生提供多个Bean模式任务Handler,可以直接配置实用,如下:

  • demoJobHandler:简单示例任务,任务内部模拟耗时任务逻辑,用户可在线体验Rolling Log等功能;
  • shardingJobHandler:分片示例任务,任务内部模拟处理分片参数,可参考熟悉分片任务;
  • httpJobHandler:通用HTTP任务Handler;业务方只需要提供HTTP链接等信息即可,不限制语言、平台。示例任务入参如下:

/**
* XxlJob开发示例(Bean模式)
*
* 开发步骤:
* 1、在Spring Bean实例中,开发Job方法,方式格式要求为 "public ReturnT<String> execute(String param)"
* 2、为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
* 3、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
*
* @author xuxueli 2019-12-11 21:52:51
*/
@Component
public class SampleXxlJob {
private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
/**
* 1、简单任务示例(Bean模式)
*/
@XxlJob("demoJobHandler")
public ReturnT<String> demoJobHandler(String param) throws Exception {
logger.info("XXL-JOB, Hello World. param={}",param);
return ReturnT.SUCCESS;
}
//...省略...
}

【重要】 如果我们要写自己的定时任务,参照上面方法,在方法上注解一个@XxlJob("任务名字") ,方法可以接受一个字符串参数,方法需要返回ReturnT格式。

最后启动执行器项目.

3.配置定时任务

3.1.执行器创建

打开调度中心可视化界面,在执行器管理界面,添加新增执行器

  • appName : 执行器的名字,可以任意填写
  • 名称:任意填写
  • 注册方式:调度中心是通过RPC的方式对执行器发起调度,所以这里需要的是执行器项目的ip:port ,注意,该端口不是执行器项目的server.port ,而是:xxl.job.executor.port 端口。你可以选择自动注册,也可以手动录入。

3.2.创建任务

在 任务管理 界面,新增任务

  • 路由策略:有轮询,随机,故障转移等等策略,是用在集群模式下的调度方式。
  • cron : 定时任务的执行时间规则,时间表达式
  • JobHandler : 这个是要对应 “执行器项目”中 @XxlJob("demoJobHandler")注解中的名字
  • 运行模式 :Bean ,使用内置代码方式,也可以执行在线执行代码方式
  • 报警邮件 :如果定时任务失败,会发送报警邮件到邮箱
  • 任务参数:这个参数可以传递给 @XxlJob("demoJobHandler")所在方法的参数。

创建好任务之后就可以执行了

调度日志

IDEA工具控制台效果

15:47:33.017 logback [Thread-16] INFO  c.x.j.e.s.jobhandler.SampleXxlJob - XXL-JOB, Hello World. param=
15:47:34.007 logback [Thread-16] INFO c.x.j.e.s.jobhandler.SampleXxlJob - XXL-JOB, Hello World. param=
15:47:35.008 logback [Thread-16] INFO c.x.j.e.s.jobhandler.SampleXxlJob - XXL-JOB, Hello World. param=
...省略...

4.GLUE模式(Java)

4.1.添加任务

该模式支持在线编辑定时任务的内容,立刻执行,无需再开发工具中编辑代码,也无需重启项目。

请点击任务右侧 “GLUE” 按钮,进入 “GLUE编辑器开发界面” ,见下图。“GLUE模式(Java)” 运行模式的任务默认已经初始化了示例任务代码,即打印Hello World。

任务以源码方式维护在调度中心,支持通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler

4.2.编写代码

保存之后可以在操作按钮里面去编写任务

(“GLUE模式(Java)” 运行模式的任务实际上是一段继承自IJobHandler的Java类代码,它在执行器项目中运行,可使用@Resource/@Autowire注入执行器里中的其他服务),比如我的定时任务如下,编辑好之后点击保存

保存好之后,启动定时任务,效果如下

三.XXL-JOB集群部署

1.调度中心集群

1.1.问题概述

调度中心支持集群部署,提升调度系统容灾和可用性。调度中心集群部署时,几点要求和建议:

  • DB配置保持一致;
  • 集群机器时钟保持一致(单机集群忽视);
  • 当启动多个调度器时,执行器配置调度中心部署跟地址可以用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;

但是建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。

1.2.启动多个调度中心

修改调度中心端口,启动多个调度中心,我这里启动两个如

  • http://localhost:18080/xxl-job-admin/
  • http://localhost:18081/xxl-job-admin/

1.3.配置Nginx负载均衡

当启动多个调度器时,执行器配置调度中心部署跟地址可以用逗号分隔。执行器将会使用该地址进行“执行器心跳注册”和“任务结果回调”;为空则关闭自动注册;

但是建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。

我们启动了2个调度中心,那么我的执行器项目该注册到哪个调度中心呢?我们通过Nginx来解决这个问题,原理如下图:

我们再hosts配置 www.jobs.com作为nginx的主机域名,然后反向代理到多个调度中心,这样一来执行器就只需要注册到www.jobs.com Nginx即可。

修改 C:\Windows\System32\drivers\etc\hosts增加配置如下

127.0.0.1 www.jobs.com

Nginx配置如下

#调度中心
upstream jobs{
server localhost:18080;
server localhost:18081;
}

server {
listen 80;
#使用域名
server_name www.jobs.com;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
#调度中心反向代理配置
proxy_pass http://jobs/;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}


}

启动Nginx,通过浏览器访问 http://www.jobs.com/xxl-job-admin/ ,可以访问到调度中心的管理界面

2.执行器项目集群

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

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

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

2.1.启动多个执行器项目

现在对执行器项目做集群,修改xxl-job-executor-sample-springboot配置文件application.properties

  • server.port : 既然是做集群,项目端口需要修改
  • xxl.job.admin.addresses : 调度中心地址需要修改成www.jobs.com ,多个执行器配置同一个地址。
  • xxl.job.executor.port : RPC通信端口也要修改,多个执行器该端口需要不一样

第一个实例配置

server.port=19090
xxl.job.admin.addresses=http://www.jobs.com/xxl-job-admin #对应Nginx地址
xxl.job.executor.port=9999

第二个实例配置

server.port=19091
xxl.job.admin.addresses=http://www.jobs.com/xxl-job-admin
xxl.job.executor.port=9998

Configurations中配置,允许启动多个实例

启动实例如下

2.2.配置定时任务

通过http://www.jobs.com/xxl-job-admin访问调度中心管理界面,在执行器管理中可以看到多台执行器实例

在任务管理中,可以编辑任务,然后选择路由策略,比如:选择轮询,然后启动任务,就会看到两个执行器项目轮着执行定时任务。

说在最后

xxl-job确实很强大,功能也很全,经过该文章学习相信你可以把xxl-job给用起来了,但是如果你的项目是一个小体量的单体,我不太建议使用它,Quzrtz或者SpringBoot Task就足够 ,对于xxl-job个人还是有些笨重。

文章目录
  1. 1. 一.定时任务概述
    1. 1.1. 1.定时任务认识
      1. 1.1.0.1. 1.1.什么是定时任务
      2. 1.1.0.2. 1.2.常见定时任务方案
  2. 1.2. 2.分布式定时任务
    1. 1.2.0.1. 2.1.遇到什么问题
    2. 1.2.0.2. 2.2.分布式定时任务xxl-job
  • 2. 二.XXL-JOB初体验
    1. 2.1. 1.xxl-job架构设计
      1. 2.1.0.1. 1.1.设计思想
      2. 2.1.0.2. 1.2.架构设计图
  • 2.2. 2.xxl-job安装
    1. 2.2.0.1. 2.1.下载源码
    2. 2.2.0.2. 2.2.导入数据库
    3. 2.2.0.3. 2.3.启动调度中心
    4. 2.2.0.4. 2.3.配置部署“执行器项目
  • 2.3. 3.配置定时任务
    1. 2.3.0.1. 3.1.执行器创建
    2. 2.3.0.2. 3.2.创建任务
  • 2.4. 4.GLUE模式(Java)
    1. 2.4.0.1. 4.1.添加任务
    2. 2.4.0.2. 4.2.编写代码
  • 3. 三.XXL-JOB集群部署
    1. 3.1. 1.调度中心集群
      1. 3.1.0.1. 1.1.问题概述
      2. 3.1.0.2. 1.2.启动多个调度中心
      3. 3.1.0.3. 1.3.配置Nginx负载均衡
  • 3.2. 2.执行器项目集群
    1. 3.2.0.1. 2.1.启动多个执行器项目
    2. 3.2.0.2. 2.2.配置定时任务
  • 4. 说在最后