定时任务模块-使用XXL-JOB改造方案.md 6.7 KB

定时任务模块:使用 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.addressesxxl.job.executor.appnamexxl.job.executor.port 等。
    • 注册一个 Bean 实现 XxlJobtenantJobDispatcher,其内部逻辑与当前 TenantJobDispatcherJob 一致:
    • 主库查启用且未过期租户;
    • for 每个租户:切库 → 加载 projectConfig → 查该租户库 sys_job 中本分钟到点任务 → 执行 JobInvokeUtil.invokeMethod(job)
    • finally 清理上下文。

2.2 与 Quartz 的衔接

  • 去掉:fs-admin 中 Quartz 的 SchedulerFactoryBeanSysJobServiceImpl.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」。

3.2 与 @Scheduled 的衔接

  • 去掉: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 并发)。

五、实施顺序建议

  1. 执行器接入:在 fs-admin、fs-qw-task 中分别引入 XXL-JOB 执行器依赖并配置,先注册一个简单测试 Job,确认 Admin 能正常调度。
  2. fs-quartz 替换:关闭 Quartz 的调度注册,保留「租户分发器」逻辑,改为由 XXL-JOB 的 tenantJobDispatcher 触发;管理端界面与 sys_job 表不变。
  3. fs-qw-task 替换:逐个去掉 @Scheduled,在 XXL-JOB Admin 中配置对应 Job 与 cron,执行器内用 TenantTaskRunner.runForEachTenant 包装原业务。
  4. 回归与监控:按租户验证任务执行、日志与数据;观察 XXL-JOB 调度日志与执行器负载。

六、与现有文档的关系

  • 租户数据源、TenantTaskRunner、按租户切库与 projectConfig 加载等,与 定时任务模块SaaS化方案.md 一致,无需因改用 XXL-JOB 而改变。
  • 本方案仅描述「调度层由 Quartz/@Scheduled 换为 XXL-JOB」的改造要点,不改变「按租户执行」的语义与实现方式。