xxl-job-架构

257 阅读6分钟

老项目之前用的是SpringBoot的定时任务,准备搭建个定时任务模块,大哥把这一块交给我来做,甚感荣幸,做完之后准备从头到尾再梳理一遍

1. 总体架构

先上一张XXL-JOB整体架构图 5a3e2cf5f066b43da8d03c9d85bcf6f7.png 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将任务信息以及日志信息持久化到数据表中,这个就保证了可以动态的添加删除任务。

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

xxl-job的大体执行逻辑

image.png

2. 调度中心

2.1 调度中心部署

调度中心可视化界面 image.png

3. 执行器

执行器负责接收调度中心的调度并执行,执行器可以单独服务,也可以将执行器内嵌到现有的服务中。
执行器默认端口为9999

3.1. 执行器的工作原理

3.1.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

  1. 注册
    • 执行器启动时会向调度中心注册自己,提供自身的地址信息(如IP和端口)。
    • 调度中心将执行器的信息保存在数据库中,以便后续任务调度时使用。
  2. 心跳
    • 执行器会定期向调度中心发送心跳包,以确认自身状态。
    • 调度中心通过心跳包判断执行器是否在线,从而决定是否将任务分配给该执行器。

3.2 任务调度

  1. 任务触发
    • 调度中心根据任务的配置(如Cron表达式)和调度策略,确定何时触发任务。
    • 当任务触发条件满足时,调度中心会从注册的执行器中选择一个合适的执行器,将任务下发给该执行器。
  2. 任务下发
    • 调度中心通过HTTP请求将任务信息发送给执行器。
    • 任务信息通常包括任务ID、参数、执行器地址等。

2.3 任务执行

  1. 任务接收
    • 执行器接收到任务请求后,解析任务信息,调用相应的任务处理器(Handler)。
    • 任务处理器是具体的业务逻辑实现。
  2. 任务执行
    • 任务处理器执行具体的业务逻辑。
    • 执行过程中,执行器可以记录任务的执行日志和结果。
  3. 结果回调
    • 任务执行完成后,执行器将结果(如成功、失败、异常信息等)通过HTTP请求回调给调度中心。
    • 调度中心记录任务的执行结果,并更新任务状态。

3. 执行器的实现细节

3.1 任务处理器(Handler)

  • 定义

    • 任务处理器是执行器中处理具体任务的类或方法。
    • 开发者需要在执行器中定义任务处理器,并将其注册到执行器中。
  • 编写任务代码

    • 通过Bean模式在后台编写任务代码。
      通常通过注解(如`@XxlJob`)或配置文件的方式注册任务处理器。:
      ```java
      @XxlJob("demoJobHandler")
      public void demoJobHandler(String param) throws Exception {
          // 业务逻辑
          System.out.println("任务执行参数: " + param);
      }
    
    • 通过GLUE模式直接在调度中心写任务脚本。 image.png image.png 点进去之后会有一个示例 image.png 通过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. 执行器的扩展性

  • 多执行器
    • 一个任务可以配置多个执行器,调度中心会根据负载均衡策略选择一个执行器执行任务。
    • 这样可以提高任务的可靠性和并发处理能力。
  • 自定义执行器
    • 开发者可以根据业务需求自定义执行器,实现更复杂的功能。
    • 例如,可以实现任务的分布式锁、任务的幂等性处理等。