如何使用消息队列构建多步骤过程?

时间:2013-02-14 04:10:53

标签: asynchronous rabbitmq message-queue amqp

假设我有一个具有这些限制的多步骤异步过程:

  1. 任何工作人员都可以执行各个步骤
  2. 必须按顺序执行步骤
  3. 我正在考虑的方法:

    1. 插入代表整个流程的数据库行,并显示“已完成步骤”列以跟踪进度。
    2. 订阅将在整个过程完成后收到消息的队列。
    3. 完成每个步骤后,更新数据库行并将该流程的下一步排队。
    4. 完成最后一步后,将“流程完成”消息排队。
    5. 删除数据库行。
    6. 思考?陷阱?更聪明的方法吗?

1 个答案:

答案 0 :(得分:10)

我已经构建了一个非常类似于您在大型任务密集型文档处理系统中描述的系统,并且在过去7年中不得不同时使用优缺点。你的方法是可靠和可行的,但我看到了一些缺点:

  • 可能容易受到状态变化的影响(即,如果在所有步骤排队之前进程输入发生变化,那么后面的步骤可能会产生与先前步骤不一致的输入)

  • 比你更基础的基础设施,涉及数据库和队列=更多的失败点,更难设置,需要更多文档=感觉不是很正确

  • 如何让多名员工同时采取同一步骤?换句话说,数据库行说完了4个步骤,工人进程如何知道它是否可以采用#5?是不是需要知道另一个进程是否已经在解决这个问题?无论如何,您需要包含额外的锁定状态。

  • 您的示例对失败很有帮助,但并不能解决并发问题。当您添加状态以解决并发问题时,故障处理成为一个严重的问题。例如,一个过程采取步骤5,然后将DB行放入" Working"州。然后,当该过程失败时,步骤5卡在"工作"状态。

  • 您的协调器有点沉重,因为它正在进行大量的同步数据库操作,我担心它可能无法像其他架构一样扩展,因为只有其中一个...这将取决于您的步骤与数据库事务相比的运行时间 - 这可能只会成为一个非常大规模的问题。

如果我重新做一遍,我肯定会将更多的编排推送到工作进程中。因此,编排代码很常见,可以由任何工作进程调用,但我会尽可能保持中央控制流程。我也只使用消息队列而不是任何数据库来保持架构简单且不太同步。

我会创建一个包含2个队列的交换:IN和WIP(正在进行中)

中央流程负责订阅流程请求,并检查WIP队列的超时步骤。

1)当中央进程收到给定处理(X)的请求时,它会调用编排代码,并将第一个任务(X1)加载到IN队列中

2)第一个可用的工作进程(P1)在事务上使X1出列,并将其排入WIP队列,并具有保守的生存时间(TTL)超时值。这种出列是原子的,IN中没有其他X任务,所以没有第二个进程可以在X任务上工作。

3)如果P1突然终止,除了超时外,地球上没有任何架构可以保存此过程。在超时期限结束时,中央流程将在WIP中找到超时X1,并将事务性地从WIP中取消X1并将其排队回IN,从而提供相应的通知。

4)如果P1异常但优雅地终止,则工作进程将事务性地从WIP中取消X1并将其入队回IN,从而提供适当的通知。根据异常,工作进程还可以选择重置TTL并重试该步骤。

5)如果P1无限期挂起或超过其TTL,则结果与#3相同。中央流程处理它,并且可能工作流程在某些时候会被回收 - 或者规则可能是在任何时候超时的情况下回收工作进程。

6)如果P1成功,那么工作进程将确定下一步,X2或X-done。如果下一步是X2,则工作进程将事务性地从WIP中取消X1,并将X2排入IN。如果下一步是X-done,则处理完成,并且可以采取适当的操作,也许这会将X-done排入IN,以供协调器进行后续处理。

我建议的方法的好处是:

  • 指定了工作进程之间的争用

  • 处理所有可能的失败情况(崩溃,异常,挂起和成功)

  • 使用RabbitMQ可以完全实现简单的体系结构,没有数据库,这使得它更具可扩展性

  • 由于工作人员处理下一步的确定和排队,因此有一个更轻量级的协调器,可以实现更具伸缩性的系统

唯一真正的缺点是它可能容易受到状态变化的影响,但通常这不是引起关注的原因。只有您可以知道这是否是您系统中的问题。

我最后的想法是:你应该有充分的理由进行这种编排。毕竟,如果进程P1完成任务X1,现在是时候某个进程可以处理下一个任务X2,那么P1似乎是一个非常好的候选者,因为它刚刚完成X1并且现在可用。按照这个逻辑,一个过程应该只是完成所有步骤直到完成 - 如果需要连续完成任务,为什么要混合和匹配过程?唯一的异步边界实际上是在客户端和工作进程之间。但我会假设您有充分的理由这样做,例如,流程可以在不同的和/或资源专用的机器上运行。