如何使用 RabbitMQ 实现可靠性?
How to achieve reliability with RabbitMQ?
我的数据存储在许多存储库中,我们希望有一组任务(又名作业)来处理这些数据。每项工作都需要访问一个或两个数据存储库。对于大文件,任务预计 运行 长达 8 小时,对于小文件,任务预计需要几毫秒。重要的是作业只执行一次并且不会错过。
我们需要在容器中设置更多代理 运行ning 以便它们执行任务。在启动时,每个代理都被授予访问一组存储库的权限。每个代理人应该 运行 只有可以完成的工作。例如,将需要访问 "R1" 和 "R2" 存储库的作业分配给只能访问 "R2"、"R3"、[= 的代理是没有意义的36=] 和 "R5".
RabbitMQ 似乎很适合这种情况。但我觉得它不可靠,原因如下:
- 同一条消息可以投递两次。
- 它可能会崩溃,因此消息可能会丢失。
- 一些代理可能会在稍后时间开始,工作可能会丢失。
我应该使用 Redis 来避免处理同一条消息两次吗?
为了实现出色的可靠性,我是否应该 运行 一个不时重新填充队列的进程?
"topic" 交换是否是将消息仅定向到可以处理它们的代理的良好解决方案?如果是,在相应代理启动之前发送消息如何处理?
当然,如果您认为其他技术比 AMQP 更适合这项工作,请随时推荐它们。
先总结一下情况:
- 您有任意数量的 unknown-length 个职位
- 作业必须恰好处理一次
- 某些作业只能运行在某些机器上
从表面上看,这似乎是 moderately-challenging job shop scheduling problem。但是,这不是您要问的。相反,您的问题似乎转向了如何确保作业只处理一次,而您正在寻找 RabbitMQ 来提供该答案。
所以让我们说清楚。 RabbitMQ 无法提供该答案,但任何其他消息队列也无法提供。有两个原因:首先,消息队列不是作业,它是作业的存放地。实际工作是代表系统状态变化的东西。消息队列只负责作业的传递,不负责作业的处理。
其次,消息代理只能真正做出两种传递保证之一。虽然您可以利用 at-most-once
(通过 auto-ack)和 at-least-once
(通过 mandatory/immediate 标志)交付,但 these two options are mathematically mutually exclusive。
要点#1:很明显,在交付机制而不是处理机制上寻找解决方案是徒劳的。
但是,有一个解决办法。
幂等性是进程的 属性,因此进程的重复应用将导致相同的状态。无论系统在流程开始时处于何种状态,流程的输出都是相同的。一个简单的例子涉及电灯开关。假设您告诉某人将电灯开关拨动 100 次,然后那个人照做了。即使假设您知道开关最初是关闭的,您能否保证开关在第 100 次翻转结束时的状态?不 - 因为世界上没有什么是完全可靠的。
但是,假设您稍微调整一下,然后说 "flip the switch to the up position." 现在,您有一个由命令产生的定义结束状态。在该过程结束时,开关将变为 "up"。多次接收此命令的人可以轻松观察开关的状态,如果开关已经处于正确状态则不采取任何操作。
如果您根据行为实现的结果而不是实现行为的过程来定义您的行为,那么您将更容易拥有一个幂等系统。因此,在 RabbitMQ 中很容易获得的 at-least-once 交付机制将在 100% 的时间内为您工作。
要点 #2:根据结果而不是过程来定义你的行为。
最后一个问题是,如何做到这一点。方式有很多种,但在none这几种方式中,消息系统是状态容器。所有计算机系统都依赖某种持久存储机制(文件、数据库、穿孔卡?)来存储和检索系统状态。您应该依靠消息来提供有关 (1) 需要做什么和 (2) 何时需要完成的提示,而不是 (3) 需要如何完成的提示。在开始由消息触发的工作之前,您必须通过检查当前状态来找出#3。
要点 #3:不要将消息队列用作状态容器。使用数据库。
我的数据存储在许多存储库中,我们希望有一组任务(又名作业)来处理这些数据。每项工作都需要访问一个或两个数据存储库。对于大文件,任务预计 运行 长达 8 小时,对于小文件,任务预计需要几毫秒。重要的是作业只执行一次并且不会错过。
我们需要在容器中设置更多代理 运行ning 以便它们执行任务。在启动时,每个代理都被授予访问一组存储库的权限。每个代理人应该 运行 只有可以完成的工作。例如,将需要访问 "R1" 和 "R2" 存储库的作业分配给只能访问 "R2"、"R3"、[= 的代理是没有意义的36=] 和 "R5".
RabbitMQ 似乎很适合这种情况。但我觉得它不可靠,原因如下:
- 同一条消息可以投递两次。
- 它可能会崩溃,因此消息可能会丢失。
- 一些代理可能会在稍后时间开始,工作可能会丢失。
我应该使用 Redis 来避免处理同一条消息两次吗?
为了实现出色的可靠性,我是否应该 运行 一个不时重新填充队列的进程?
"topic" 交换是否是将消息仅定向到可以处理它们的代理的良好解决方案?如果是,在相应代理启动之前发送消息如何处理?
当然,如果您认为其他技术比 AMQP 更适合这项工作,请随时推荐它们。
先总结一下情况:
- 您有任意数量的 unknown-length 个职位
- 作业必须恰好处理一次
- 某些作业只能运行在某些机器上
从表面上看,这似乎是 moderately-challenging job shop scheduling problem。但是,这不是您要问的。相反,您的问题似乎转向了如何确保作业只处理一次,而您正在寻找 RabbitMQ 来提供该答案。
所以让我们说清楚。 RabbitMQ 无法提供该答案,但任何其他消息队列也无法提供。有两个原因:首先,消息队列不是作业,它是作业的存放地。实际工作是代表系统状态变化的东西。消息队列只负责作业的传递,不负责作业的处理。
其次,消息代理只能真正做出两种传递保证之一。虽然您可以利用 at-most-once
(通过 auto-ack)和 at-least-once
(通过 mandatory/immediate 标志)交付,但 these two options are mathematically mutually exclusive。
要点#1:很明显,在交付机制而不是处理机制上寻找解决方案是徒劳的。
但是,有一个解决办法。
幂等性是进程的 属性,因此进程的重复应用将导致相同的状态。无论系统在流程开始时处于何种状态,流程的输出都是相同的。一个简单的例子涉及电灯开关。假设您告诉某人将电灯开关拨动 100 次,然后那个人照做了。即使假设您知道开关最初是关闭的,您能否保证开关在第 100 次翻转结束时的状态?不 - 因为世界上没有什么是完全可靠的。
但是,假设您稍微调整一下,然后说 "flip the switch to the up position." 现在,您有一个由命令产生的定义结束状态。在该过程结束时,开关将变为 "up"。多次接收此命令的人可以轻松观察开关的状态,如果开关已经处于正确状态则不采取任何操作。
如果您根据行为实现的结果而不是实现行为的过程来定义您的行为,那么您将更容易拥有一个幂等系统。因此,在 RabbitMQ 中很容易获得的 at-least-once 交付机制将在 100% 的时间内为您工作。
要点 #2:根据结果而不是过程来定义你的行为。
最后一个问题是,如何做到这一点。方式有很多种,但在none这几种方式中,消息系统是状态容器。所有计算机系统都依赖某种持久存储机制(文件、数据库、穿孔卡?)来存储和检索系统状态。您应该依靠消息来提供有关 (1) 需要做什么和 (2) 何时需要完成的提示,而不是 (3) 需要如何完成的提示。在开始由消息触发的工作之前,您必须通过检查当前状态来找出#3。
要点 #3:不要将消息队列用作状态容器。使用数据库。