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

Mat*_*att 7 performance activemq-classic jms

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

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

  1. 许多生产者将消息放在一个队列中.消息可以具有不同的JMSXGroupID
  2. 许多消费者在该单个队列上收听消息
  3. 作为标准实践,JMSXGroupID分布在消费者中
  4. 在某些时候,其中一个消费者变得很慢,无法很快处理消息
  5. 慢速消费者最终在代理上填充其预取缓冲区,AMQ认识到它很慢(默认行为)
  6. 在那一点 - 或者某些'随机'但稍后关闭时间 - 除慢速消费者之外的所有消费者开始仅以相同的30秒间隔消费消息
  7. 如果缓慢的消费者再次变得快速,那么事情很快就会恢复正常运行,30秒的障碍就会消失

我对可能导致此问题的原因感到茫然,或者如何修复它,请帮忙.

更多背景和发现

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

Mat*_*att 5

虽然严格来说不是问题的解决方案,但进一步的调查发现了这个问题的根本原因。

TL;DR - 这是已知行为,不会在Apollo之前修复

更多细节

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

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

为了阻止pagedInPendingDispatch集合耗尽所有可用资源,建议限制通过maxPageSize属性配置的此队列的大小。这个属性实际上并不是一个最大值,它更多的是暗示在正常情况下,新消息到达是应该在内存中分页还是分页到磁盘。

有了这两条信息和一个慢消费者,结果最终pagedInPendingDispatch集合中的所有消息最终只能由慢消费者使用,因此该集合被有效地阻塞,并且没有其他消息被调度。这解释了为什么慢消费者不受 30 秒间隔的影响,它已经有maxPageSize消息等待传递。

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

所以我们现在拥有的是内存中的一组消息,这些消息都是针对分派给同一个消费者的,消费者不会接受它们,因为它很慢或被阻塞。我们还有大量等待发送给所有消费者的消息。每expireMessagesPeriod毫秒都会有一个任务运行,强制将消息分页到内存中,以检查它们是否应该过期。这会将这些消息添加到集合中的页面上,这些页面现在包含maxPageSize发送给慢速消费者的N消息和更多发往任何消费者的消息。这些消息被传递。

QED。

参考