XXL-JOB最佳实践与升级指南

前言:

xxl-job是国内一款使用者比较多的分布式任务调度平台,我们内部从19年开始使用该款开源软件,使用的是2.0.1版本,并在此基础上做了二次开发,添加了一些定制化的功能。随着使用该定时器的业务组越来越多,有些业务组提出想要使用xxl-job新版本中引入的一些功能。因此,我们着手考虑了xxl-job的升级方案。

通过该文章,你可以学习到如下内容:

1. xxl-job的版本从2.0.1升级到2.3.1存在不兼容的问题,而且生产环境已经在使用,如何做到无缝迁移

2. xxl-job中主要功能的具体处理流程和底层原理,遇到问题可以快速排查

3. xxl-job在使用的过程中有哪些注意事项,避免踩坑

升级方案选型:

xxl-job升级面临的问题

  1. xxl-job的版本从2.0.1升级到2.3.1存在不兼容的问题,而且生产环境已经在使用,如何做到无缝迁移
  2. 内部使用的过程中进行过二次开发,对代码进行过修改,如何顺利迁移已经修改过的逻辑

xxl-job升级步骤

  1. 检查xxl-job-core是否向后兼容

经过测试发现,xxl-job-core的2.0.1版本无法成功注册到xxl-job-admin上面去。

为了能够实现升级的平滑过渡,我们考虑了如下三个方案:

(1)修改xxl-job-admin的源代码为了能够保证已有job的正常工作,使得2.3.1版本的xxl-job-admin同时支持2.0.1版本和2.3.1两个版本的xxl-job-core

(2)修改xxl-job-core的代码使得2.3.1版本的xxl-job-core同时识别2.0.1版本和2.3.1版本的注解和配置

(3)admin端和core端都不做修改,升级后各个业务自己修改JobHandler的相关代码

经过调研分析,我们决定采用第二种修改方案,这样我们的改动比较小,而且对各个业务方也基本上是透明的,基本不用对代码进行修改。

  1. 检查数据存储对应关系,编写数据迁移脚本

2.0.1版本的数据库表如下:

XXL_JOB_QRTZ_BLOB_TRIGGERS

Quartz框架中使用的数据表,该项目中目前未使用到

XXL_JOB_QRTZ_CALENDARS

同上

XXL_JOB_QRTZ_CRON_TRIGGERS

存储定时任务的cron信息和时区信息

XXL_JOB_QRTZ_FIRED_TRIGGERS

Quartz框架中存储当前已经运行的触发器状态信息

XXL_JOB_QRTZ_JOB_DETAILS

Quartz框架中存储触发器的详细信息

XXL_JOB_QRTZ_LOCKS

Quartz框架中的悲观锁

XXL_JOB_QRTZ_PAUSED_TRIGGER_GRPS

Quartz框架中暂停的触发器组信息

...................................................

忽略Quartz框架下的其他数据库表,Quartz框架下的数据表和数据迁移工作内容无关

XXL_JOB_QRTZ_TRIGGER_GROUP

定时任务所属分组,也就是各个工程的相关信息

XXL_JOB_QRTZ_TRIGGER_INFO

定时任务的基本信息

XXL_JOB_QRTZ_LDAP_GROUP

LDAP分组与定时任务分组的关联关系表(二次开发添加)

XXL_JOB_QRTZ_TRIGGER_LOGGLUE

以GLUE模式运行的定时任务对应的源码信息

XXL_JOB_QRTZ_TRIGGER_REGISTRY

注册的执行器信息

XXL_JOB_QRTZ_TRIGGER_LOG

记录定时任务的调度结果,这里需要注意的时候,调度成功并不代表着该定时任务执行成功。

2.3.1版本xxl-job数据库表信息如下:

xxl_job_group

对应2.0.1版本中XXL_JOB_QRTZ_TRIGGER_GROUP数据表

xxl_job_info

对应2.0.1版本中的XXL_JOB_QRTZ_TRIGGER_INFO

xxl_job_lock

JobScheduleHelper通过该数据库表实现对定时任务的互斥调度;如果要实现一个简单的分布式调度器,实现JobScheduleHelper这个类基本就够了

xxl_job_log

对应2.0.1版本中的XXL_JOB_QRTZ_TRIGGER_LOG表

xxl_job_log_report

定时任务调度结果的汇总报表

xxl_job_logglue

对应2.0.1版本中的XXL_JOB_QRTZ_TRIGGER_LOGGLUE表

xxl_job_registry

对应2.0.1版本中的XXL_JOB_QRTZ_TRIGGER_REGISTRY表

xxl_job_user

新增的用户信息表,在我们内部不会使用到

手动新增:xxl_job_ldap_group

存储各个技术组的信息

注:有关Quartz框架中数据库表字段解释可以参考这篇技术博客:

https://www.cnblogs.com/zyulike/p/13671130.html

3. 修改2.3.1版本的xxl-job-admin

这个步骤里面的主要工作是迁移和添加二次开发的代码到新版本的xxl-job-admin,主要功能包括:登录逻辑修改,权限管理逻辑修改,邮件报警通知修改成企业微信通知,执行器和定时任务进行分组管理,针对不合理的逻辑进行性能优化。

4. 修改2.3.1版本的xxl-job-core

修改xxl-job-core的代码使得2.3.1版本的xxl-job-core同时识别2.0.1版本和2.3.1版本的注解和配置

注意事项:

  1. 如果在JobHandler实现类中有需要进行一次性初始化的操作,务必避免使用init()这个方法,因为JobHandler每次运行的时候都会重复执行init()方法
  2. 定时任务执行失败可以通过企业微信通知相关的负责人,注意需要抛异常出来才可以
  3. 注册的执行器节点最好使用自动注册的方式

xxl-job定时任务处理流程:

v.2.0.1版本控制台侧xxl-job手动触发定时任务的处理流程:

自动触发定时任务是通过XxlJobDynamicSchedulerConfig中创建的SchedulerFactoryBean来调度完成

v2.3.1版本控制台侧手动触发定时任务的处理流程:

 

 

自动触发定时任务是通过XxlJobAdminConfig中创建的XxlJobScheduler来调度完成的

v2.0.1版本业务侧定时任务的处理流程如下:

 

 

v2.3.1版本业务侧定时任务的处理流程和v2.0.1版本的处理流程并没有太多的区别,主要的差异在于EmbedServer从JettyServer切换成了NettyServer,底层信息的协议格式依然是HTTP协议。

 

新版本主要功能底层实现原理解析:

功能解析一:

调度线程池隔离,拆分为”Fast”和”Slow”两个线程池,1分钟窗口期内任务耗时达500ms超过10次,该窗口期内判定为慢任务,慢任务自动降级进入”Slow”线程池,避免耗尽调度线程,提高系统稳定性

上述功能是通过在JobScheduleHelper中设置两个线程池来实现的,不过实在不太理解这个功能的作用是什么,因为只要调度成功给业务侧的EmbedServer后EmbedServer就会返回成功的消息给控制台一端,而不需要等待定时任务具体逻辑真正执行结束。

功能解析二:自研调度组件,移除quartz依赖:一方面是为了精简系统降低冗余依赖,另一方面是为了提供系统的可控度与稳定性;

该自研的调度组件是依赖JobScheduleHelper来完成的,具体处理流程如下所示:

功能解析三:GLUE-JAVA模式的定时任务是如何工作的

通过Groovy ClassLoader将Java源码动态编译成为class,具体处理可以查看GlueFactory类

总结:

今天就写到这里吧,如果有想继续深入沟通的小伙伴儿,请加微信群聊或者本人微信进一步沟通和交流。

               

posted @ 2022-08-30 20:53  K太狼  阅读(847)  评论(0编辑  收藏  举报
在页面定制 CSS 代码中添加如下代码: /*生成博客目录*/ #uprightsideBar{ font-size: 16px; font-family: Arial, Helvetica, sans-serif; text-align: left; position: fixed; /*将div的位置固定到距离top:250px,right:0px的位置,这样div就会处在最右边的位置,距离顶部50px*/ top: 376px; left: 0px; /*确定在左侧还是右侧*/ width: auto; height: auto; } #sideBarTab { float: left; width: 40px; border: 4px solid #ED5736; border-left: none; text-align: center; background: #ffffff; font-size: 22px; } #sideBarContents{ float: left; overflow: auto; overflow-x: hidden;!important; width: 300px; min-height: 100px; max-height: 500px; border: 1px solid #e5e5e5; border-left: none; background: #ffffff; } #sideBarContents dd, dt { cursor: pointer; } .title1:hover, .title2:hover, .title3:hover, .title4:hover, .title5:hover, .title6:hover { color: #ED5736; } #sideBarContents dl{ margin: 0; padding: 0; } .title1{ margin-top: 5px; margin-left: 5px; } .title2{ margin-left: 20px; } .title3{ margin-left: 40px; } .title4{ margin-left: 60px; } .title5{ margin-left: 80px; } .title6{ margin-left: 100px; }