队列公平性和消息服务器

时间:2016-05-23 19:05:02

标签: jms rabbitmq messaging

我希望通过消息服务器和队列的FIFO特性来解决我遇到的问题。在某些情况下,我希望将队列中的消息按照其传递的消息顺序以外的标准分发到消费者池中。理想情况下,这会阻止用户占用系统中的共享资源。采取这种过于简化的方案:

  • 应用程序中有一项功能,用户可以清空垃圾桶。
  • 此事件为垃圾箱中的每个项目发送DELETE消息
  • 此队列的使用者调用具有速率限制API的Web服务。

鉴于每个用户的垃圾桶中都有大量的邮件,我们有什么选择可以同时处理每个垃圾桶,而不考虑入队时间?在我看来,有一些明显的解决方案:

  • 为每个用户创建单独的队列和使用者池
  • 将消息传递从单个队列随机化为单个消费者池

在我们的例子中,创建一个单独的队列并为每个用户管理消费者实际上并不实际。可以这样做,但我认为如果合理,我真的更喜欢第二种选择。我们正在使用RabbitMQ,但如果有更适合此任务的技术,则不一定与它相关。

我很乐意使用Rabbit的消息优先级来帮助随机化交付。通过随机分配1到10之间的优先级的消息,这应该有助于分发消息。此方法的问题在于,如果队列永远不会被完全清空,那么具有最低优先级的消息可能永远停留在队列中。我以为我可以在邮件上使用TTL,然后以升级优先级重新排队邮件,但我在docs注意到了这一点:

  

应该过期的邮件仍然只会从头部过期   队列。这意味着与普通队列不同,甚至是每个队列   TTL可能导致过期的低优先级消息卡在后面   未过期的优先级较高的。这些消息永远不会   已发送,但它们将显示在队列统计信息中。

我担心我会用这种方法走向兔子洞。我想知道其他人是如何解决这个问题的。任何有关广告素材路由,消息传递模式或任何替代解决方案的反馈都会得到应用。

3 个答案:

答案 0 :(得分:1)

一种解决方案可能是设置Resequencer。该链接中的诊断概述了该原则。在你的情况下,像:

  • 应用程序将其DELETE消息最初调度到删除队列中。
  • Resequencer(您编写的新组件)介于原始发布者和原始使用者之间。它:

    • 将邮件从DELETE队列中拉入内存
    • 将它们放入(内存中)队列中的用户
    • 将它们重新发布到新队列(例如FairPriorityDeleteQueue),进行循环以相当地交错来自不同原始用户的任何消息
    • 将其重新发布速率限制为FairPriorityDeleteQueue,或者使得FairPriorityDeleteQueue的长度(通过定期轮询rabbitmq管理api可获得)永远不会超过您选择N的某个整数,或者限制为与速率限制删除API相关的某些速率消费者使用。
    • 不会删除它从原始DELETE队列中删除的任何消息,直到它重新发布到FairPriorityDeleteQueue(因此您永远不会丢失消息)
  • 原始消费者订阅了FairPriorityDeleteQueue。

    • 您将这些使用者的preFetchCount设置得相当低(<10),以防止他们在内存中批量缓冲FairPriorityDeleteQueue的内容。

-

要注意的一些要点:

  • 从FairPriorityDeleteQueue中发布和/或绘制消息的速率或长度限制至关重要。如果您没有限制,Resequencer可能只是在收到消息时发送消息,限制了重新测序的可能性。
  • 当重新排序时,重定序器当然是一种内存缓冲区。如果原始发布者可以突然将非常大量的消息发布到队列中,您可能需要对重定序器进程进行内存限制,以使其不会摄取超过它可以容纳的内容。 / LI>

您的特定情况得益于您有一个限制吞吐量的外部因素(最终删除API)。如果没有这样的外在限制因素,选择最佳参数会更加困难对于这样的重定序器,要在特定环境中平衡吞吐量与重新排序。

答案 1 :(得分:1)

在这种情况下,我不认为需要重新定序器。也许是这样,如果您需要确保按特定顺序删除项目。但是,只有在大致相同的时间发送多条消息并且需要保证消费者端的订单时才会发挥作用。

由于您提到的原因,您还应该避免超时情况。超时是为了告诉RabbitMQ消息不需要处理 - 或者它需要被路由到死信队列,以便我可以通过其他一些代码处理。虽然你可能能够暂停工作,但我认为这不是一个好的选择。

优先级可能会解决部分问题,但可能会引入一个文件永远不会被处理的场景。如果您在某个队列中有一条优先级为1的消息,并且您继续将优先级2,3,5,10等放入队列中,则可能无法处理1。正如你所指出的那样,超时并没有解决这个问题。

对于我的钱,我建议采用不同的方法:为单个文件连续发送删除请求。

即发送1条消息删除1个文件。等待回应说完了。然后发送下一条消息删除下一个文件。

这就是为什么我认为这会起作用,以及如何管理它:

长时间运行的工作流程,单个文件删除请求

在这种情况下,我建议采用一个多步骤的方法解决问题,使用#34; saga&#34; (又名a long-running workflow object)。

当用户请求删除其垃圾箱时,您通过rabbitmq向可以处理删除过程的服务发送单个消息。该服务为该用户的垃圾桶创建了一个传奇实例。

saga收集垃圾箱中需要删除的所有文件的列表。然后它开始发送删除单个文件的请求,一次一个。

每次删除单个文件的请求时,saga都会等待响应,说该文件已被删除。

当saga收到消息说上一个文件已被删除时,它会发出下一个删除下一个文件的请求。

删除所有文件后,saga会更新自身以及系统的任何其他部分,以说垃圾桶是空的。

处理多个用户

当您有一个用户请求删除时,事情会很快发生。他们很快就会把垃圾清空。

u1 = User 1 Trashcan Delete Request

|u1|u1|u1|u1|u1|u1|u1|u1|u1|u1done|

当您有多个用户请求删除时,一次发送一个文件删除请求的过程意味着每个用户都有相同的机会获得下一个文件删除。

u1 = User 1 Trashcan Delete Request
u2 = User 2 Trashcan Delete Request

|u1|u2|u1|u1|u2|u2|u1|u2|u1|u2|u2|u1|u1|u1|u2|u2|u1|u2|u1|u1done|u2|u2done|

这样,共享使用资源来删除文件。总而言之,每个人的垃圾箱需要更长的时间才能被清空,但他们会更快地看到进展,这是人们认为系统快速/响应他们的要求的一个重要方面。

优化小文件集与大文件集

如果您的用户数量较少且文件数量较少,则上述解决方案可能会比您一次删除所有文件的速度慢。毕竟,将通过rabbitmq发送更多消息 - 每个需要删除的文件至少有2个消息(一个删除请求,一个删除确认响应)

为了进一步优化,您可以做以下几件事:

  1. 在拆分这样的工作之前有一个最小的垃圾桶大小。低于该最低要求,您只需将其全部删除

  2. 将工作分成多组文件,而不是一次一个。也许10个或100个文件是一个更好的组大小,一次比1个文件

  3. 这些解决方案中的任何一个(或两者)都有助于通过减少发送的消息数量和批量处理工作来提高整个过程的性能。

    您需要在真实场景中进行一些测试,以了解哪些(或两者)可以提供帮助,以及在什么设置下。

    许多用户问题

    您可能面临的另一个问题是 - 许多用户。如果您有2或3个用户请求删除,那么这将不是一件大事。

    但是如果你有100或1000个用户请求删除,那么个人可能需要很长时间才能清空他们的垃圾桶。

    对于这种情况,您可能需要更高级别的控制流程,其中所有空垃圾桶的请求都将由另一个Saga管理。这个传奇将限制活跃的垃圾桶删除传奇的数量。

    例如,如果您有10个活动请求来删除垃圾桶,那么速率限制传奇只会启动其中的3个,并且会在开始下一个之前等待一个完成。

    同样,出于性能原因,您需要测试您的实际场景,看看是否需要这样做并查看限制应该是什么。

    在您的实际场景中可能还有其他场景需要考虑,但我希望这会让您走上正轨! :)

答案 2 :(得分:1)

所以我最终从网络路由器手册中取出了一个页面。这是路由器需要解决的问题,以允许公平的流量模式。 This video可以很好地解决问题和解决方案。

将问题转化为我的域名:

Unfair Flows with a Single FIFO Queue

解决方案:

Fair Flows with Multiple FIFO Queues and a Load Balancer

负载均衡器是一个信道包装器和一个已知数量的队列,它们使用加权算法来平衡每个队列上收到的消息。到目前为止,我们发现really interesting article/implementation似乎运行良好。

使用此解决方案,我还可以在发布消息后确定工作空间的优先级,以提高其吞吐量。这是一个非常好的功能。

我面临的最大挑战是排队管理。将有太多队列离开交易所很长一段时间。我正在研究一些工具来管理他们的生命周期。