老项目之前用的是SpringBoot的定时任务,准备搭建个定时任务模块,大哥把这一块交给我来做,甚感荣幸,做完之后准备从头到尾再梳理一遍
1. 总体架构
先上一张XXL-JOB整体架构图
XXL-JOB 的架构主要包括两部分:调度中心(Admin)和执行器(Executor)。
- 中心化的调度中心(Admin): 负责管理任务的调度策略、任务的状态、任务的日志等。 提供可视化Web 界面进行任务管理。 存储任务的元数据和执行日志。
- 分布式的执行器(Executor): 负责具体任务的执行。 可以部署在不同的机器上,支持分布式部署。 通过 HTTP 或其他协议与调度中心通信,接收任务执行指令。
调度中心本身不承担业务逻辑,只负责发起调度请求,由执行器接收调度请求并执行对应JobHandler中的业务逻辑,通过将调度和任务解耦,提高系统的可扩展性(意思是方便新增curd任务)。
1.1 哪些痛点需要引入xxl-job
- SpringBoot下的@Scheduled定时任务:
- 任务的停止与执行时间调整都得依靠发版。
- 任务执行没有统计、失败报警、监控
- 定时任务执行时间长(如http的超时时间设置较长)导致线程阻塞,影响其他定时任务执行。
- 在分布式环境下会导致定时任务重复执行
- Quartz:配置太复杂了。当时在O配个定时任务给我累半死。
- elasticJob:引入了Zookeeper,同样提高了复杂度
1.2 xxl-job的优势
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:系统用户表;
xxl-job的大体执行逻辑
2. 调度中心
2.1 调度中心部署
调度中心可视化界面
3. 执行器
执行器负责接收调度中心的调度并执行,执行器可以单独服务,也可以将执行器内嵌到现有的服务中。
执行器默认端口为9999
3.1. 执行器的工作原理
3.1.1 注册与心跳
- 配置: 在yml文件中的配置
# 调度中心地址,多个地址用逗号分隔
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
# 执行器名称,唯一标识
xxl.job.executor.appname=xxl-job-executor-sample
# 执行器地址,如果为空则自动获取
xxl.job.executor.ip=
# 执行器端口,如果为空则自动获取
xxl.job.executor.port=9999
# 执行器访问令牌,用于安全验证
xxl.job.accessToken=
# 日志路径
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
# 日志保留天数
xxl.job.executor.logretentiondays=30
# 线程池核心线程数
xxl.job.executor.threadpool.coreSize=10
# 线程池最大线程数
xxl.job.executor.threadpool.maxSize=200
# 线程池队列大小
xxl.job.executor.threadpool.queueSize=2000
# 线程池线程空闲时间(秒)
xxl.job.executor.threadpool.keepAliveTime=60
# 任务超时时间(秒)
xxl.job.executor.timeout=60
# 任务失败重试次数
xxl.job.executor.retryCount=3
# 任务失败重试间隔(秒)
xxl.job.executor.retryInterval=10
- 注册:
- 执行器启动时会向调度中心注册自己,提供自身的地址信息(如IP和端口)。
- 调度中心将执行器的信息保存在数据库中,以便后续任务调度时使用。
- 心跳:
- 执行器会定期向调度中心发送心跳包,以确认自身状态。
- 调度中心通过心跳包判断执行器是否在线,从而决定是否将任务分配给该执行器。
3.2 任务调度
- 任务触发:
- 调度中心根据任务的配置(如Cron表达式)和调度策略,确定何时触发任务。
- 当任务触发条件满足时,调度中心会从注册的执行器中选择一个合适的执行器,将任务下发给该执行器。
- 任务下发:
- 调度中心通过HTTP请求将任务信息发送给执行器。
- 任务信息通常包括任务ID、参数、执行器地址等。
2.3 任务执行
- 任务接收:
- 执行器接收到任务请求后,解析任务信息,调用相应的任务处理器(Handler)。
- 任务处理器是具体的业务逻辑实现。
- 任务执行:
- 任务处理器执行具体的业务逻辑。
- 执行过程中,执行器可以记录任务的执行日志和结果。
- 结果回调:
- 任务执行完成后,执行器将结果(如成功、失败、异常信息等)通过HTTP请求回调给调度中心。
- 调度中心记录任务的执行结果,并更新任务状态。
3. 执行器的实现细节
3.1 任务处理器(Handler)
-
定义:
- 任务处理器是执行器中处理具体任务的类或方法。
- 开发者需要在执行器中定义任务处理器,并将其注册到执行器中。
-
编写任务代码:
- 通过Bean模式在后台编写任务代码。
通常通过注解(如`@XxlJob`)或配置文件的方式注册任务处理器。: ```java @XxlJob("demoJobHandler") public void demoJobHandler(String param) throws Exception { // 业务逻辑 System.out.println("任务执行参数: " + param); }- 通过GLUE模式直接在调度中心写任务脚本。
点进去之后会有一个示例
通过GLUE模式执行的类会被实例化,并执行其execute()方法
3.2 任务执行线程池
- 线程池:
- 执行器内部使用线程池来管理任务的执行。
- 线程池可以提高任务的并发处理能力,避免因单个任务阻塞导致其他任务无法执行。
- 配置:
- 线程池的配置可以通过配置文件或代码进行设置,例如最大线程数、队列大小等。
# 线程池核心线程数
xxl.job.executor.threadpool.coreSize=10
# 线程池最大线程数
xxl.job.executor.threadpool.maxSize=200
# 线程池队列大小
xxl.job.executor.threadpool.queueSize=2000
# 线程池线程空闲时间(秒)
xxl.job.executor.threadpool.keepAliveTime=60
3.3 任务超时与重试
- 超时:
- 执行器可以配置任务的最大执行时间,超过该时间任务将被标记为超时。
- 超时任务可以被调度中心记录并处理。
- 重试:
- 执行器可以配置任务的重试机制,当任务执行失败时,可以自动重试指定次数。
- 重试次数和间隔可以通过配置文件或代码进行设置。
3.3. 执行器的扩展性
- 多执行器:
- 一个任务可以配置多个执行器,调度中心会根据负载均衡策略选择一个执行器执行任务。
- 这样可以提高任务的可靠性和并发处理能力。
- 自定义执行器:
- 开发者可以根据业务需求自定义执行器,实现更复杂的功能。
- 例如,可以实现任务的分布式锁、任务的幂等性处理等。