是"公平排队"可以使用JMS

时间:2014-10-15 08:49:27

标签: jms hornetq

我需要实现一个公平的排队系统,以便根据某些邮件头的值,以当前排队的邮件中该标头的所有值,以循环方式处理邮件。

系统中的消息自然按某些属性分组,其中有数千个可能的值,当前排队的消息的值集随时间而变化。类比将是具有标题的消息,该标题是在消息创建时的毫秒部分时间。因此,标头的值将介于0和999之间,并且当前排队的所有消息中都会有一些值的分布。

我需要能够按顺序使用消息,使得没有特定值优先于任何其他值。如果排队消息的头值是这样分发的

value | count
------|-------
  A   |   3
  B   |   3
  C   |   2

然后消费订单为A,B,C,A,B,C,A,B

如果将具有其他值的消息添加到队列中,则应将它们自动添加到循环序列中。

这意味着对当前排队的消息有一些了解,但并不要求消费者持有这些知识;经纪人可能有以某种方式订购交货的机制。

在公平排队开始之前有一些阈值是可以接受的。也就是说,如果阈值为10,那么顺序处理具有相同值的10个消息是可接受的,但处理的第11个消息应该依次是 next 值。如果唯一排队的消息具有该值,则 Next 可能是相同的值。

可能值的数量可能会排除为每个队列创建一个队列,并迭代队列,尽管尚未经过测试。

我们正在使用HornetQ,但是如果有其他提供这些语义的替代方案,那么我很乐意知道。

消息是作业,标头值是用户ID。正在寻求的是,在某些限制内,任何给定用户的工作都不会过度延迟任何其他用户的工作;生产100万个工作的用户不会导致其他用户的后续工作等待处理这一百万个工作。

HornetQ队列中的消费者按创建顺序进行评估,因此在队列中添加选择性消费者不会阻止任何全能消费者接收与过滤器匹配的消息。

JMS群组似乎没有帮助,因为这会将给定群体(用户?)与给定的消费者联系起来。

潜在的解决方案是根据需求在主题上创建选择性消费者(例如:来自同一用户的10个连续消息),管理所有选择性消费者的生命周期,以确保所有选择消费者不会处理同样的消息。虽然可能这似乎有一些繁重的同步要求。

4 个答案:

答案 0 :(得分:0)

要考虑的第一个选择是拥有一个多线程消费应用程序。假设每个会话/消费者有一个线程,就可以使用选择器设置同步或异步接收。每个选择器都将键入特定用户。

假设JVM在线程分派方面是合理的(我很乐意假设)并且应用程序代码中没有任何死锁,我认为这些要求将被填满。一个线程可能会被用户百万个工作困住,其余的不会受到影响。

但是,如果需要单线程应用程序,那么JMS规范中的任何内容都无济于事。他们可能是供应商扩展,当然可以帮助。但是,另一个选项是应用程序查看每个消息并将其设置为用户标识的特定队列。最终消费应用程序本身将在这些队列之间“循环”以获得工作。需要另一个应用程序,但你有一个非常确定的系统。

答案 1 :(得分:0)

我建议使用消息优先级来设置consumerWindowSize = 0并始终让客户端从服务器中选择一条新消息。

您的邮件会有更多延迟,但您始终会收到来自服务器的邮件。

请注意,您需要考虑种族。如果你消费消息C和消息B到达怎么办?然后你会消耗C以供以后消费B.但是由于C已经消耗不足,你可以做的并不多。

您也可以考虑使用邮件分组,但是您可以通过负载平衡将消息组绑定到单个使用者。

答案 2 :(得分:0)

您希望JMS代理实现消息传递算法(公平排队),据我所知,该算法不是JMS规范的一部分。有可能找到让代理人这样做的方法,但我对此表示怀疑,而你提出的任何解决方案都可能是特定于代理的。

相反,为什么不将所需的排队算法放在您自己的应用程序中?例如:写一个"公平排队转发器(FQF)"订阅所有消息的应用程序,无论它们来自代理的顺序如何。让此FQF应用程序尽可能快地使用消息,以便JMS代理队列始终为空或接近空。然后,FQF应用程序可以将消息存储在本地队列中,并按照所需排队算法确定的顺序,一次一个地重新发布消息到最终消息处理应用程序订阅的队列或主题。为此,您可能希望使用事务或某种流控制,以便FQF应用程序仅以最终系统可以处理的速率发布消息。

根据您的示例,这些消息表示要根据邮件头中的用户ID属性按特定顺序处理的作业。所以我建议您编写一个作业调度算法,使用您想要的任何排队算法将作业传递给作业处理器。

通过这种方式,您可以完全控制消息处理顺序,而无需以某种方式"技巧"经纪人做你想做的事。您将JMS简单地用作消息传递机制,因此您不必编写自己的消息传递协议。

答案 3 :(得分:0)

不确定我是否完全理解这一点,但是问题在于您的媒体资源具有数千个可能的值,并且随着时间的推移会发生变化。这听起来像是一个典型的“拯救哈希值”问题。那么如何创建该属性的哈希值,然后对该值进行模运算以得出一个公平的但先前已知的值呢?

让我们假设在100个队列(从Q0到Q99)中处理100个消费者是可行的。然后,您可以在JMS生产者中这样做:

String queueName = "Q" + user.hashCode()%100;

这是生产者发送到的队列名称。用户值也作为属性添加。相同的用户将进入相同的队列(如果队列太多,则排队),并且用户几乎公平地分布在使用中的应用程序中。

现在您仍然遇到一个坏用户创造一百万个工作岗位的问题。此解决方案的第二部分可能是启动时为空的JMS选择器。第一条消息用完后,您需要计算每个用户的工作数量,一旦达到阈值,例如来自同一用户的10个作业,您可以通过添加选择器来暂时禁止该用户,例如:'user NOT LIKE user123'。如果有不止一次这样的用户,则使用“ AND”累加选择。一旦使用者没有收到其他消息,您可以将该使用者的选择设置为空,然后再次开始处理队列。

相关问题