一个缓慢的ActiveMQ消费者导致其他消费者变慢

时间:2014-05-22 15:14:27

标签: performance jms activemq

我正在寻找有关奇怪问题的帮助,其中队列上的慢消费者导致同一队列中的所有其他消费者以30秒的间隔开始消费消息。这是所有消费者,但速度慢的消费者并没有尽可能快地消费消息,而是在消费之前等待一些神奇的30秒屏障。

我的应用程序的基本流程如下:

  1. 许多制作人将消息放入单个队列。消息可以具有不同的JMSXGroupID
  2. 许多消费者在该单个队列上收听消息
  3. 作为标准做法,JMSXGroupID分布在消费者身上
  4. 在某个时刻,其中一个消费者变得很慢,无法快速处理消息
  5. 慢速消费者最终在代理上填充其预取缓冲区,AMQ认识到它很慢(默认行为)
  6. 此时 - 或某些'随机'但稍后关闭 - 除慢速消费者之外的所有消费者开始仅以相同的30秒间隔消费消息
  7. 如果缓慢的消费者再次变得快,那么事情很快就会恢复正常运作,而30秒的障碍就会消失
  8. 我对可能导致此问题的原因感到茫然,或者如何修复它,请帮忙。

    更多背景和调查结果

    • 我已经设法在AMQ 5.8.0,5.9.0(最初发现问题的地方)和5.9.1上可靠地重现了这个问题,关于全新安装和现有操作管理安装,以及在不同机器上的某些虚拟机和有些不是。所有Linux安装,不同操作系统和Java版本。
    • 它似乎不受任何预取相关的影响,即:将预取值从1更改为10到1000并未阻止问题发生
    • [red herring?]在amq实例上启用调试日志会显示与定期检查可能过期的消息相关的日志。该队列没有到期策略,因此我只能认为预定的expireMessagesPeriod时间只是以某种方式唤醒amq,然后它会向非慢速消费者发送消息。
    • 如果输入30s模式然后再次输入,则秒数 - 分钟时间始终相同,例如分钟后的14s和44s。所有消费者和托管这些消费者的所有机器都是如此。重新启动amq后,这些障碍点会发生变化。

1 个答案:

答案 0 :(得分:4)

虽然不是问题的严格解决方案,但进一步的调查已经揭示了这个问题的根本原因。

TL; DR - 这是已知的行为,在Apollo

之前不会修复

更多详情

最终这是由maxPageSize属性以及AMQ仅将选择条件应用于内存中的消息引起的。通常这些是消息选择器(property = value),但在我的情况下,它们是JMSXGroupID=>Consumer分配。

当队列收到消息时,它们会被分页到内存中并放入一个集合(源代码中名为pagedInPendingDispatch)。要发送消息,AMQ将扫描此消息列表并尝试查找将接受它的消费者。这包括检查组ID,消息选择器和预取缓冲区空间。对于我们的用例,我们不使用消息选择器,但我们正在使用组。如果没有消费者可以接收消息,那么它将留在集合中,并在下一个时间点再次检查。

为了阻止pagedInPendingDispatch集合占用所有可用资源,建议限制通过maxPageSize属性配置的此队列的大小。这个属性实际上并不是最大值,它更像是一个提示,在正常情况下,是否应该在内存中分页新邮件或将其分页到磁盘。

有了这两条信息和一个缓慢的消费者,事实证明最终pagedInPendingDispatch集合中的所有消息最终只能被慢消费者消费,因此集合被有效地阻止而没有其他消息派遣。这解释了为什么慢速消费者不受30秒间隔的影响,它已经有maxPageSize条消息等待递送。

这并不能解释为什么我看到非慢速消费者每30秒收到一次消息。事实证明,将消息分页到内存有两种模式,普通强制。正常遵循上面概述的过程,其中集合的大小与maxPageSize属性进行比较,强制时,但是,消息始终被分页到内存中。此模式用于允许您浏览不在内存中的消息。在发生这种情况时,到期机制也会使用强制模式,以允许AMQ使不在内存中的消息过期。

所以我们现在拥有的是内存中的一组消息,这些消息都是针对发送给同一个消费者的消费者,消费者由于速度慢或被阻止而不接受消费者。我们还有积压的消息等待交付给所有消费者。每隔expireMessagesPeriod毫秒执行一个任务,强制将消息分页到内存中,以检查它们是否应该过期。这会将这些消息添加到集合中的页面上,这些消息现在包含maxPageSize消息,用于缓慢消费者和N个消息,这些消息将发往任何消费者。这些消息将被传递。

QED。

<强>参考