预取限制的作用是什么?
ActiveMQ Classic 的设计目标之一是成为高性能的消息总线。这意味着使用 SEDA 架构来尽可能异步地执行尽可能多的工作。为了有效利用网络资源,代理使用“推送”模型将消息分发到消费者。这确保消费者始终有一个本地消息缓冲区,可以随时处理。另一种方法是消费者显式地从代理中拉取消息。单独拉取消息效率不高,并且会显著增加每条消息的延迟。
但是,如果不限制推送给消费者的消息数量,消费者的客户端资源可能会耗尽。这是消息消费通常比消息传递慢得多的自然结果。为了避免这种情况,ActiveMQ Classic 因此使用 **预取限制** 来限制可以一次分发给单个消费者的最大消息数量。消费者反过来使用预取限制来调整其预取消息缓冲区的大小。
一旦代理向消费者分发了预取限制数量的消息,它将不会向该消费者分发任何更多消息,直到消费者确认至少 50% 的预取消息(例如,prefetch/2)为止。当代理收到上述确认时,它将向消费者分发另外 prefetch/2 条消息,以“补充”其预取缓冲区。请注意,可以在每个消费者的基础上指定预取限制(见下文)。
对于高性能和大消息量,建议使用较大的预取值。但是,对于较低的消息量,其中每条消息需要很长时间来处理,则应将预取设置为 1。这确保消费者一次只处理一条消息。但是,指定为零的预取限制会导致消费者轮询消息,一次一条,而不是消息被推送到消费者。
什么是慢速消费者?
慢速消费者是指挂起的待处理消息数量超过其配置的预取限制的两倍的消费者。
使用动态语言实现消费者
无法缓存预取消息的消费者必须将其预取设置为 1。例如,使用诸如 Ruby 之类的脚本语言通过 STOMP 连接实现的消费者。在这种情况下,没有客户端消息缓冲区的概念。
指定 PrefetchPolicy
您可以在 ActiveMQPrefetchPolicy 的实例上指定 ActiveMQConnectionFactory 或 ActiveMQConnection。这允许您配置所有单独的预取值;因为每种不同的服务质量都有不同的值。例如
-
持久队列(默认值:
1000
) -
非持久队列(默认值:
1000
) -
持久主题(默认值:
100
) -
非持久主题(默认值:
Short.MAX_VALUE - 1
)
还可以在用于建立与代理连接的连接 URI 上配置预取限制。要更改所有消费者类型的预取限制,请配置以下连接 URI
tcp://127.0.0.1:61616?jms.prefetchPolicy.all=50
要仅更改队列消费者的预取限制,请配置以下连接 URI
tcp://127.0.0.1:61616?jms.prefetchPolicy.queuePrefetch=1
它也可以使用 目标选项 在每个消费者的基础上进行配置
queue = new ActiveMQQueue("TEST.QUEUE?consumer.prefetchSize=10");
consumer = session.createConsumer(queue);
池化消费者和预取
由于预取,从消费者池中消费消息可能会出现问题。未消费的预取消息只有在消费者关闭时才会释放,但是对于池化消费者,关闭将推迟(以供重用)直到消费者池关闭。这会导致预取消息在消费者被重用之前未被消费。从性能的角度来看,此功能可能是可取的。但是,当池中有多个消费者时,这会导致消息传递顺序错误。出于这个原因,org.apache.activemq.pool.PooledConnectionFactory **不** 会池化消费者。
Springs CachingConnectionFactory 支持池化消费者(尽管默认情况下已关闭)。如果您在 Springs DefaultMessageListenerContainer (DMLC) 中配置了多个消费者线程并使用 CachingConnectionFactory,那么您要么希望在 CachingConnectionFactory 中关闭消费者池化(默认情况下已关闭),要么可能希望在池化消费者时使用 0 的预取。这样,消费者将在每次调用 receive(timeout)
时轮询消息。通常建议关闭 Springs CachingConnectionFactory 中的消费者缓存以及允许池化 JMS 消费者的任何其他框架。
请注意,Springs DefaultMessageListenerContainer (DMLC) 及其 CACHE_CONSUMER
缓存级别不受此问题的影响!Springs DMLC 不会像使用内部池包含多个消费者实例那样池化消费者。相反,它会缓存消费者,即它会重用同一个 JMS 消费者对象来接收 DMLC 实例生命周期内的所有消息。因此,它的行为与正确编写的 JMS 代码非常相似,您在其中创建 JMS 连接、会话、消费者,然后使用此消费者实例来接收所有消息。
因此,在 Springs DMLC 中使用 CACHE_CONSUMER
不会有任何问题,即使使用多个消费者线程也是如此,除非您使用 XA 事务。XA 事务不适用于 CACHE_CONSUMER
。但是,本地 JMS 事务和非事务性消费者在 Springs DMLC 中使用 CACHE_CONSUMER
是完全可以的。
另外请注意,Camel 的 JMS 或 ActiveMQ Classic 组件在内部使用 Springs DMLC。因此,上述关于 Springs DMLC 和 CACHE_CONSUMER
的所有内容也适用于这两个 Camel 组件。
内存与性能权衡
设置相对较高的预取值会导致更高的性能。因此,默认值通常大于 1000,对于主题来说更大,对于非持久性消息来说更大。预取大小决定了客户端在 RAM 中保留多少条消息,因此,如果您的 RAM 有限,您可能希望设置较低的值,例如 1 或 10 等。