接收消息
接收消息总是比发送稍微复杂一些。接收 Message 有两种方式。较简单的方式是用一个轮询方法调用一次轮询一条 Message。较复杂但更常见的方式是注册一个监听器,按需异步接收 Message。我们在接下来的两个小节中分别考察这两种方式的示例。
本节总结
轮询消费者
AmqpTemplate 本身可用于轮询式 Message 接收。默认情况下,如果没有可用消息,会立即返回 null。没有阻塞。从 1.5 版开始,你可以设置一个以毫秒为单位的 receiveTimeout,接收方法会阻塞至多该时长,等待消息。小于零的值表示无限期阻塞(或至少直到与 broker 的连接断开)。1.6 版引入了 receive 方法的变体,允许在每次调用时传入超时。
异步消费者
prefetch 的默认值过去是 1,这可能导致高效消费者得不到充分利用。从 2.0 版开始,默认 prefetch 值现在是 250,这在大多数常见场景下应能让消费者保持忙碌,从而提高吞吐量。
批量消息
批量消息(由生产者创建)会被监听容器自动拆批(使用 springBatchFormat 消息头)。拒绝批次中的任何一条消息都会导致整批被拒绝。有关批处理的更多信息,请参阅批处理。
消费者事件
每当监听器(消费者)经历某种故障时,容器都会发布应用事件。事件 ListenerContainerConsumerFailedEvent 具有以下属性:
消费者标签
你可以提供一个生成消费者 tag 的策略。默认情况下,消费者 tag 由 broker 生成。下面的清单展示了 ConsumerTagStrategy 接口定义:
注解驱动的监听器端点
15 个项目
带批处理的 @RabbitListener
当接收一批消息时,拆批通常由容器执行,监听器一次接收一条消息被调用。从 2.2 版开始,你可以配置监听容器工厂和监听器,使其在一次调用中接收整批消息——只需将工厂的 batchListener 属性设为 true,并让方法的载荷参数为 List 或 Collection:
使用容器工厂
引入监听容器工厂是为了支持 @RabbitListener 以及向 RabbitListenerEndpointRegistry 注册容器,如编程式端点注册中所讨论。
异步 @RabbitListener 返回类型
@RabbitListener(以及 @RabbitHandler)方法可以指定异步返回类型 CompletableFuture<?> 和 Mono<?>,从而让回复被异步发送。ListenableFuture<?> 不再受支持;它已被 Spring Framework 废弃。
线程与异步消费者
异步消费者会涉及若干不同的线程。
选择容器
2.0 版引入了 DirectMessageListenerContainer(DMLC)。此前,只有 SimpleMessageListenerContainer(SMLC)可用。SMLC 为每个消费者使用一个内部队列和一个专用线程。如果一个容器被配置为监听多个队列,则用同一个消费者线程处理所有队列。并发度由 concurrentConsumers 和其他属性控制。当消息从 RabbitMQ 客户端到达时,客户端线程通过队列把它们转交给消费者线程。这种架构在当时是必要的,因为在 RabbitMQ 客户端的早期版本中,无法进行多个并发投递。较新版本的客户端有了修订后的线程模型,现在可以支持并发。这使得 DMLC 得以引入,现在监听器直接在 RabbitMQ 客户端线程上被调用。因此,它的架构实际上比 SMLC 更"简单"。然而,这种方式存在一些限制,SMLC 的某些特性在 DMLC 上不可用。此外,并发度由 consumersPerQueue(以及客户端库的线程池)控制。concurrentConsumers 及相关属性在此容器上不可用。
检测空闲的异步消费者
异步消费者虽然高效,但有一个问题是如何检测它们何时空闲——如果一段时间内没有消息到达,用户可能希望采取某些行动。
Micrometer 集成
从 2.2 版开始,如果 classpath 上检测到 Micrometer,且应用上下文中存在唯一一个 MeterRegistry(或者恰好有一个被标注为 @Primary,例如使用 Spring Boot 时),监听容器会自动为监听器创建并更新 Micrometer Timer。可以通过将容器属性 micrometerEnabled 设为 false 来禁用这些计时器。
Micrometer 观测
从 3.0 版开始,现在对 RabbitTemplate 和监听容器支持使用 Micrometer 进行观测。