camunda job调度 - modric2jeff/archive GitHub Wiki

好的 👍 我给你整理一份 Camunda Job 生成与调度机制 的说明,并配上图例(时序/架构图),帮助你清晰理解 Job 的生成时机调度时机


🔹 Camunda Job 的生成与调度流程

1. Job 生成时机

Camunda 在以下场景会生成 Job 记录(存储到 ACT_RU_JOB 表):

  • BPMN asyncBefore / asyncAfter

    • 执行到带 asyncBefore=true 的活动 → 生成 Job,当前事务提交后才可执行。
  • Timer events(Timer Start / Boundary / Intermediate / Cycle)

    • 部署流程时或运行时 → 生成 Job,等待到期触发。
  • Failed job 重试

    • Job 执行失败,根据重试策略生成新的 Job。
  • External Task

    • 创建 ExternalTask 时,也会在 ACT_RU_EXT_TASK,它和 Job 表类似。

👉 生成特点

  • 生成 Job 的动作是在 CommandContext 内部完成的。
  • Job 会被写入数据库,但必须等到 事务提交,才能被 JobExecutor 线程“看见”。

2. Job 调度时机

调度由 JobExecutor 控制,JobExecutor 是一个后台线程池,流程是:

  1. Job 查询

    • 周期性扫描 ACT_RU_JOB 表,查询 duetime <= now and suspended = false 的 Job。
  2. Job 获取

    • 使用 acquireJobsCmd 上锁,设置 lockOwnerlockExpirationTime,防止并发抢。
  3. Job 执行

    • 提交到线程池,调用对应的 JobHandler
    • 如果成功,删除 Job;如果失败,生成重试 Job 或 Incident。

👉 注意:

  • 事务提交前生成的 Job → JobExecutor 还看不见。
  • 事务提交后 → JobExecutor 可能立即扫描到并调度。
  • 如果此时你在 AFTER_COMMIT 去 suspend instance,可能会和 JobExecutor 抢时间。

🔹 图解说明

图 1:Job 生成与事务提交

+--------------------+             +-------------------+
| 用户触发流程执行   |             | CommandContext    |
| (进入某个节点)    |             |                   |
+--------------------+             +-------------------+
           |                                 |
           | asyncBefore=true                |
           v                                 |
  生成 Job (内存中) -----------------------> | insert into ACT_RU_JOB
           |                                 |
           | (还未提交)                      |
           v                                 |
       提交事务 ---------------------------> 数据库持久化

👉 要点:Job 真正可见是在 事务提交之后


图 2:JobExecutor 调度流程

        +--------------------+
        |   JobExecutor      |
        | (后台线程池)       |
        +--------------------+
                 |
      周期扫描 ACT_RU_JOB
                 |
                 v
       找到 duetime <= now
      且 suspended = false
                 |
        Acquire (加锁)
                 |
                 v
         提交线程池执行
                 |
                 v
        JobHandler 处理逻辑
                 |
       成功 -> 删除Job
       失败 -> 重试/Incident

图 3:Race Condition(挂起 vs Job调度)

时间轴 →
-------------------------------------------------------------

1. 事务提交 -> Job 写入数据库
2. JobExecutor 正在扫描数据库
3. AFTER_COMMIT 挂起流程实例
4. JobExecutor 抢先锁到 Job
5. 执行时发现流程已挂起 -> 抛出 SuspendedException

👉 这就是为什么 deleteJob/suspendInstance 无法保证阻止 Job 执行一次。 因为 JobExecutor 抢走的瞬间,数据库里还没标记 suspended。


🔹 总结

  1. 生成时机

    • Job 在 流程引擎执行命令中生成,直到 事务提交后 才能被调度。
  2. 调度时机

    • JobExecutor 周期性查询数据库,按 dueTime 获取 Job 并执行。
  3. Race condition

    • suspend/delete 和 JobExecutor 获取之间存在竞态。
    • 所以 Job 可能执行一次后才被中止。

要不要我帮你画一个 更清晰的时序图(事务提交 + JobExecutor调度),用 PlantUML 或 Mermaid 代码形式,你可以直接放在文档里生成图?