代码之家  ›  专栏  ›  技术社区  ›  user3429660

如何使用RabbitMQ实现可靠性?

  •  4
  • user3429660  · 技术社区  · 6 年前

    我的数据存储在许多存储库中,我们希望有一组任务(即作业)来处理这些数据。每个作业都需要访问一个或两个数据存储库。对于大文件,任务的运行时间预计为8小时,对于小文件,任务的运行时间预计为几毫秒。重要的是,作业只执行一次,而且不会遗漏。

    我们需要设置更多在容器中运行的代理,以便它们执行任务。在启动时,每个代理都被授予访问一组存储库的权限。每个代理应仅运行能够完成的作业。例如,将需要访问“R1”和“R2”存储库的作业分配给只能访问“R2”、“R3”、“R4”和“R5”的代理毫无意义。

    RabbitMQ似乎是这个场景的一个很好的候选者。但我觉得它不可靠,原因如下:

    • 它可以两次传递相同的信息。
    • 它可能会崩溃,因此消息可能会丢失。
    • 一些代理可能会在稍后的时间启动,工作可能会丢失。

    我应该使用Redis来避免两次处理同一条消息吗?

    为了获得极好的可靠性,我是否应该运行一个不时重新填充队列的进程?

    “主题”交换是否是一个很好的解决方案,可以将消息仅定向到可以处理它们的代理?如果是这样的话,如何处理在相应代理启动之前发送消息的情况?

    当然,如果您认为其他技术比AMQP更适合这项工作,请随意推荐。

    1 回复  |  直到 6 年前
        1
  •  7
  •   theMayer    6 年前

    让我们首先总结一下情况:

    • 您有任意数量的未知长度作业
    • 作业必须只处理一次
    • 某些作业只能在某些计算机上运行

    从表面上看,这似乎是一个适度的挑战 job shop scheduling problem 。然而,这不是你要问的。相反,您的问题似乎是针对如何确保作业只处理一次,您正在寻找RabbitMQ来提供这个答案。

    所以让我们说清楚。RabbitMQ无法提供该答案,但任何其他消息队列也无法提供该答案。这有两个原因:第一,消息队列不是作业,它是作业的存放处。实际作业表示系统中状态的变化。消息队列只负责 传送 工作,而不是 正在处理 工作的一部分。

    其次,消息代理只能真正做出两种交付保证中的一种。而你可以利用 at-most-once (通过自动确认)和 at-least-once (通过强制/即时标志)交付, these two options are mathematically mutually exclusive

    外卖#1 : 显然,在交付机制而不是处理机制中寻找解决方案是徒劳的。

    但是,有一个解决方案。

    幂等性是一个过程的性质,重复应用该过程将导致相同的状态。无论系统在流程开始时处于何种状态,流程的输出都是相同的。一个简单的例子是一个电灯开关。假设你告诉某人把电灯开关拨100次,而这个人就这样做了。即使你知道开关最初是关闭的,你能保证在第100次翻转结束时开关的状态吗?不,因为世界上没有什么东西是完全可靠的。

    但是,假设您稍微调整一下,然后说“将开关翻转到向上位置”现在,您已经定义了一个由命令生成的结束状态。在过程结束时,开关将处于“向上”状态。多次收到此命令的人员可以轻松观察开关的状态,如果开关已处于正确状态,则不采取任何行动。

    如果您根据行为所达到的结果来定义自己的行为,而不是根据实现行为的过程来定义自己的行为,那么您将能够更好地拥有一个幂等系统。因此,RabbitMQ中很少提供的至少一次交付机制将在所有情况下为您工作。

    外卖#2 : 根据结果而不是过程来定义你的行为。

    最后一个问题是,如何做到这一点。有很多种方法,但消息系统都不是状态容器。所有计算机系统都依赖某种持久存储机制(文件、数据库、穿孔卡?)存储和检索系统状态。你应该依靠这些信息来提供提示,说明(1)需要做什么,(2)什么时候需要做,而不是(3)如何做。在开始由消息触发的工作之前,您必须通过检查当前状态来确定#3。

    外卖#3 : 不要将消息队列用作状态容器。使用数据库。