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

mar*_*lar 5 asynchronous message-queue amqp rabbitmq

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

  1. 任何工人都可以执行各个步骤
  2. 步骤必须按顺序执行

我正在考虑的方法:

  1. 插入表示整个过程的数据库行,并显示"已完成步骤"列以跟踪进度.
  2. 订阅将在整个过程完成后收到消息的队列.
  3. 完成每个步骤后,更新db行并排队该过程的下一步.
  4. 完成最后一步后,排队"进程完成"消息.
  5. 删除db行.

思考?陷阱?更聪明的方法吗?

J.T*_*lor 10

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

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

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

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

  • 您的示例对于失败很有效,但不能解决并发问题.当您添加状态以解决并发问题时,故障处理成为一个严重的问题.例如,进程采用步骤5,然后将DB行置于"工作"状态.然后当该过程失败时,步骤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并且现在可用.按照这个逻辑,一个过程应该只是完成所有步骤直到完成 - 为什么混合和匹配过程如果需要连续完成任务?唯一的异步边界实际上是在客户端和工作进程之间.但我会假设您有充分的理由这样做,例如,流程可以在不同的和/或资源专用的机器上运行.

  • 是的,我认为这是对的.想要将未来的任务放入MQ中是很诱人的,这与消息队列的语义不一致 - 例如,您可能会在某个时刻在队列中的某个位置插入任务.这感觉就像在数据库级别更适当地做了一些事情,然后你有一个严格的语义,即主动准备处理的任务在队列中,而那些不在DB中的任务. (2认同)