为什么需要xxl-job

3,100 阅读5分钟

一 为什么需要分布式任务调度系统

Timer、ScheduledThreadPoolExecutor,定时任务都在当前进程中被执行;如果该应用是集群部署,即分布式任务,同一任务将被所有节点并发执行。

欢迎参考我之前的文章
Java定时任务之Timer
Java定时任务之ScheduledThreadPoolExecutor

因此,分布式系统中使用Timer、ScheduledThreadPoolExecutor,常常需要同步措施(如刷新应用本地缓存的定时任务,就不需要做同步)。

通常使用全局锁方式:可以是DB锁,也可以是分布式锁。某个任务执行流程变为:获取全局锁,成功则执行任务,最后释放锁;否则放弃执行。

使用全局锁避免分布式任务并发执行,存在哪些不足呢?

  • 全局锁实现依赖于第三方中间件,其可靠性不受自己完全控制;
  • 当任务很多时,需维护相同数量的全局锁,有些繁重;
  • 如何使用全局锁,是在业务代码中实现的,如果未能正确加锁、解锁,将导致故障;
  • 当应用节点较多时,每次任务的执行,都会出现激烈的锁争抢;

此外,本地定时任务还存在这些不足:

  • 不支持任务参数的动态调整
  • 不支持分片任务
  • 较难失败重试,或失效转移
  • 任务执行数据难以统计

因此,我们需要一款适用于分布式系统的任务调度系统。

二 XXL-JOB快速入门

2.1 简介

XXL-JOB是由大众点评许雪里开源的一个分布式任务调度平台。目前已被广泛使用,项目的gitHub地址:github.com/xuxueli/xxl…

xxl-job将任务定义、执行和调度职责拆分,形成执行器、调度器两个组件:

  • 执行器即应用程序,一个应用中可以定义多个承载业务逻辑的任务,这些任务将由应用线程来执行;
  • 调度器负责维护执行器列表、任务触发、日志收集等,独立于应用程序。 image.png

官方文档中介绍xxl-job有很多特性,比如:

  1. 简单:支持通过Web页面对任务进行CRUD操作,操作简单;
  2. 调度中心HA(中心式):即高可用,能够集群部署;
  3. 弹性扩容缩容::一旦有新执行器机器上线或者下线,调度中心能及时感知,并调整任务路由;
  4. 丰富的路由策略:包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
  5. 任务控制:包括修改任务状态和配置,超时中断,启动/停止任务,失败重试、告警等;
  6. 用户及权限管理。

2.2 项目结构

下载项目源码,用idea打开,看到项目结构如下。 xxj1.png

  • xxl-job-admin:调度中心,统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台;
  • xxl-job-core:公共依赖,是执行器的主要实现;
  • xxl-job-executor-samples:执行器示例,有多个版本,java编程常用的有
    • xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐使用该方式;
    • xxl-job-executor-sample-spring:Spring版本,通过Spring容器管理执行器,比较通用;
    • xxl-job-executor-sample-frameless:无框架版本。

2.3 创建数据库表

在doc/db目录中,提供了sql脚本。主要的表有:

  1. xxl_job_info,记录任务的配置信息
  2. xxl_job_registry执行器注册表
  3. xxl_job_group执行器列表,就是业务应用列表
  4. xxl_job_user,做用户权限管理
  5. xxl_job_lock调度中心锁;调度中心可集群化部署,使用DB锁防止并发调度,表中有且仅有一条记录;
  6. xxl_job_log,记录日志摘要信息,日志本身以文件形式保存在执行器所在的机器。

如果是8.0+版本的MySQL,创建xxl_job_registry表时,会遇到如下报错:索引字段长度太长,超过了767bytes

UTF8中一个varchar是三字节,索引 i_g_k_v 中包含的三个字段总长度50+255+255=560个varchar,即1680byte。

此时,可以通过修改字段长度来解决报错:如registry_key改为varchar(64)registry_value改为varchar(128)image-20221122220257762.png

2.4 启动调度中心

在/xxl-job/xxl-job-admin/src/main/resources/application.properties中,只修改数据库配置:

### 调度中心JDBC链接
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

调度中心访问地址:http://localhost:8080/xxl-job-admin ,默认登录账号(初始化SQL脚本创建的)为admin,密码123456image.png

SQL脚本已创建了一个执行器、一个任务,只是机器列表为空。 image.png image.png

2.5 启动执行器

xxl-job-executor-sample-springboot是springboot框架下的执行器示例项目。修改application.properties:

// 应用端口
server.port=8081
// 调度中心地址
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
// 与调度中心通信的端口
xxl.job.executor.port=9999
// 日志文件保存路径
xxl.job.executor.logpath=/data/xxl-job/jobhandler
// 日志保存天数
xxl.job.executor.logretentiondays=5

启动项目,该应用将注册到调度平台。 image.png

2.6 手动触发任务

在任务管理页面,将“测试任务1”手动执行一次。 image.png 查看本次的日志:调度日志、执行日志 image.png image.png image.png

三 总结

Timer、ScheduledThreadPoolExecutor不适用于分布式系统,除非采取额外的同步措施,来防止节点间并发执行。本文介绍了一种分布式任务调度平台——xxl-job,并演示了如何快速使用它。

下一篇,我们将通过源码,一同学习执行器的实现细节,也欢迎大家查看我之前的文章:xxl-job服务注册浅析