定时任务模块:使用 XXL-JOB 改造方案(仅方案)
在现有「Quartz + @Scheduled + 按租户顺序执行」的 SaaS 定时任务基础上,若调度层改为 XXL-JOB,可参考本方案。只给出改造思路与要点,不涉及具体代码实现。
一、XXL-JOB 与当前架构的对应关系
| 当前 |
使用 XXL-JOB 后 |
| Quartz 调度器(内嵌在 fs-admin) |
XXL-JOB Admin 中心:负责 cron 配置、触发、路由到执行器 |
| 分发器 Job(TenantJobDispatcherJob)每分钟执行 |
XXL-JOB 中注册一个 Job,cron 为每分钟,执行器执行「租户分发 + 执行各租户 sys_job」 |
| fs-qw-task 的 @Scheduled 方法 |
每个原定时任务在 XXL-JOB 中对应一个 Job(或一个 Job + 不同 jobParam 区分),执行器内仍用 TenantTaskRunner.runForEachTenant(业务逻辑) |
原则:调度由 XXL-JOB 负责;按租户切库、按租户执行业务的逻辑保留在执行器应用内,不交给 XXL-JOB 做租户维度的调度。
二、fs-quartz 部分(管理端可配置任务)
2.1 角色划分
- XXL-JOB Admin:只配置一条任务,例如
- JobHandler:
tenantJobDispatcher
- Cron:
0 * * * * ?(每分钟)
- 路由策略、阻塞策略、失败重试等按需配置。
- 执行器:部署在 fs-admin 所在应用(或与 fs-admin 同进程)。
- 引入
xxl-job-core / xxl-job-spring-boot-starter,配置 xxl.job.admin.addresses、xxl.job.executor.appname、xxl.job.executor.port 等。
- 注册一个 Bean 实现
XxlJob 的 tenantJobDispatcher,其内部逻辑与当前 TenantJobDispatcherJob 一致:
- 主库查启用且未过期租户;
- for 每个租户:切库 → 加载 projectConfig → 查该租户库 sys_job 中本分钟到点任务 → 执行
JobInvokeUtil.invokeMethod(job);
- finally 清理上下文。
2.2 与 Quartz 的衔接
- 去掉:fs-admin 中 Quartz 的
SchedulerFactoryBean、SysJobServiceImpl.init() 里对 Quartz 的注册(或通过配置关闭 Quartz 调度,仅保留执行器逻辑)。
- 保留:
- sys_job / sys_job_log 仍在各租户库;
JobInvokeUtil、CronUtils、ScheduleUtils 等仍由执行器在「租户分发」逻辑中调用;
- 管理端「定时任务」界面仍对当前登录租户库的 sys_job 做 CRUD,不依赖 Quartz 内存态,仅需说明「实际触发由 XXL-JOB 统一每分钟执行分发器」即可。
2.3 小结
- 谁触发:XXL-JOB Admin 按 cron 触发「租户分发器」这一条任务。
- 谁执行:执行器(fs-admin 进程)执行「查主库租户列表 → 按租户切库 → 执行各租户到点 sys_job」。
- 顺序/并行:与现有一致,建议仍为按租户顺序执行,避免 ThreadLocal 与连接池在多线程下复杂化;若后续需要并行,再在执行器内用线程池对「每个租户」提交独立任务并在子线程内设租户上下文。
三、fs-qw-task 部分(企微等固定节奏任务)
3.1 角色划分
- XXL-JOB Admin:为每个原 @Scheduled 任务在 Admin 中配置一条 Job。
- 例如:
qwCheckSopRuleTime(cron:每天 1:10)、addTag(每 20 分钟)、sendQwGroupMsgTask(每 10 分钟)等,与现有 cron 保持一致。
- JobHandler 命名可与方法名一致,便于维护。
- 执行器:部署在 fs-qw-task 应用。
- 引入 XXL-JOB 执行器依赖,配置 appname、admin 地址、端口等。
- 每个 XXL-JOB Job 对应一个 XxlJob 实现类(或一个统一 Dispatcher 根据 jobParam 路由到不同业务方法),在方法内部调用 TenantTaskRunner.runForEachTenant(原有业务逻辑),保证仍是「按租户顺序执行、每租户切库+加载 projectConfig」。
- 去掉:fs-qw-task 中所有业务类上的
@Scheduled,以及 @EnableScheduling(若不再需要)。
- 保留:
- TenantTaskRunner、TenantDataSourceManager、按租户切库与 ProjectConfig 加载逻辑;
- 原有业务方法(如
qwSopService.checkSopRuleTime())不变,仅由「XXL-JOB 的 JobHandler」调用 tenantTaskRunner.runForEachTenant(() -> qwSopService.checkSopRuleTime()) 这类形式。
- SaaS 开关:可保留
saas.task.enabled。当为 true 时,XXL-JOB 触发的 Job 内部用 runForEachTenant;为 false 时可直接执行业务方法(单租户),便于兼容。
3.3 小结
- 谁触发:XXL-JOB Admin 按各任务配置的 cron 触发对应 Job。
- 谁执行:fs-qw-task 作为执行器,收到调度后执行「TenantTaskRunner.runForEachTenant(具体业务)」。
- 顺序/并行:与现有一致,默认按租户顺序;若需并行,再在 TenantTaskRunner 或执行器层增加「按租户并行」的可选实现。
四、统一说明(顺序 / 并行)
- 当前方案(Quartz + @Scheduled):按租户顺序执行,无多线程并行。
- 改为 XXL-JOB 后:仅把「谁在何时触发」从 Quartz/Spring 换成 XXL-JOB;执行器内部仍建议先保持「按租户顺序」执行,与现有一致。
- 若日后需要「多租户并行」:
- 可在执行器内用线程池,每个租户一个任务,在子线程中先设置该租户的 DynamicDataSourceContextHolder + TenantConfigContext,再执行业务,finally 清理。
- 需注意连接池大小、超时与失败重试,以及 XXL-JOB 的「阻塞策略」「路由策略」是否与多线程兼容(例如选「单机串行」避免同一 Job 并发)。
五、实施顺序建议
- 执行器接入:在 fs-admin、fs-qw-task 中分别引入 XXL-JOB 执行器依赖并配置,先注册一个简单测试 Job,确认 Admin 能正常调度。
- fs-quartz 替换:关闭 Quartz 的调度注册,保留「租户分发器」逻辑,改为由 XXL-JOB 的
tenantJobDispatcher 触发;管理端界面与 sys_job 表不变。
- fs-qw-task 替换:逐个去掉 @Scheduled,在 XXL-JOB Admin 中配置对应 Job 与 cron,执行器内用 TenantTaskRunner.runForEachTenant 包装原业务。
- 回归与监控:按租户验证任务执行、日志与数据;观察 XXL-JOB 调度日志与执行器负载。
六、与现有文档的关系
- 租户数据源、TenantTaskRunner、按租户切库与 projectConfig 加载等,与 定时任务模块SaaS化方案.md 一致,无需因改用 XXL-JOB 而改变。
- 本方案仅描述「调度层由 Quartz/@Scheduled 换为 XXL-JOB」的改造要点,不改变「按租户执行」的语义与实现方式。