目录
消息转换:MessageConverter 和 MessagePropertiesConverter消息
官方文档地址
- Spring官网:Spring AMQP
- 中文文档:版本2.3.11,要注意版本区别http://geekdoc.top/docs/languages/java/spring/spring-amqp/2.3.11/reference/html/index.html
- springboot集成AMQP:Messaging
- RabbitMQ可配置属性:Common Application Properties
AMQP抽象(消息、交换机、队列、绑定)
- Message类:为了方便参数传递和操作,AMQP定义的通用领域模型,但是在0-9-1 AMQP规范中没有定义该类或接口。
其中,两个关键属性:
- private final MessageProperties messageProperties:用于定义常见属性、头信息等;
- private final byte[] body:消息体
- 在实际开发过程中,我们经常调用convertAndSend函数发送消息,此时入参可以为Object类型,在RabbitTemplate内部,会调用convertMessageIfNecessary方法,将消息转换为Message类,然后再进行发送。
- 从版本 1.5.7、1.6.11、1.7.4 和 2.0.0 开始,如果 消息体是一个序列化的 Serializable java 对象,在执行 toString() 操作(例如在日志消息中)时不再反序列化(默认情况下)。 这是为了防止不安全的反序列化。 默认情况下,只有 java.util 和 java.lang 类被反序列化。 要恢复到以前的行为,您可以通过调用 Message.addAllowedListPatterns(… ) 添加允许的类/包模式。 支持简单的 通配符,例如 com.something., *.MyClass. 无法反序列化的主体在日志消息中由 byte[ ] 表示。
- Exchange接口:定义了交换机的基本属性,并且根据类型有相应的默认实现。
- Queue类:定义了队列的相关属性。可以通过流API:QueueBuilder进行绑定。
- Binding类:定义了绑定相关的属性和操作。可以通过流API:BindingBuilder进行绑定。
- 更多的抽象可以在org.springframework.amqp.core中查看。
资源管理
连接工程厂:管理与 RabbitMQ 代理的连接的核心组件是 ConnectionFactory 接口。ConnectionFactory实现的职责是提供一个 org.springframework.amqp. rabbit.connection. Connection 的实例。共有三种连接工厂可供选择。
- PooledChannelConnectionFactory:
- 对于大多数用例,应该使用 PooledChannelConnectionFactory。如果您想确保严格的消息排序而不需要使用 作用域操作,可以使用 ThreadChannelConnectionFactory。
- 该工厂基于 Apache Pool2 管理单个连接和两个通道池。 一个池用于事务通道,另一个池用于非事务通道。 池是具有默认配置的 GenericObjectPool,提供回调以配置池。
- ThreadChannelConnectionFactory
这个工厂管理一个连接和两个 ThreadLocal ,一个用于事务性通道,另一个用于非事务性通道。 该工厂确保同一线程上的所有操作都使用相同的通道(只要它保持打开状态)。 这有助于严格的消息排序,而无需作用域操作。 为避免内存泄漏,如果您的应用程序使用许多短期线程,您必须调用工厂的 closeThreadChannel() 来释放通道资源。 从版本 2.3.7 开始,一个线程可以将其通道传输到另一个线程。
- CachingConnectionFactory:
- 默认情况下,它会建立一个可由应用程序共享的连接代理。 共享连接是可能的,因为使用 AMQP 进行消息传递的“工作单元”实际上是一个“通道”(在某些方面,这类似于 JMS 中连接和会话之间的关系)。 连接实例提供了一个 createChannel 方法。 CachingConnectionFactory 实现支持对这些通道进行缓存,并根据通道是否为事务性维护单独的缓存。 创建 CachingConnectionFactory 的实例时,您可以通过构造函数提供“主机名”。 您还应该提供“用户名”和“密码”属性。 要配置通道缓存的大小(默认为 25),您可以调用 setChannelCacheSize() 方法。
- CachingConnectionFactory 现在通过 getCacheProperties() 方法提供缓存统计信息。 这些统计信息可用于调整缓存以在生产中对其进行优化。
- 如果你想使用相关的发布者确认或者如果你想通过它的 CacheMode打开多个连接,应该使用 CachingConnectionFactory。【SpringBoot默认工厂】
所有三个工厂都支持简单的发布者确认。
- 发布者确认和返回
- 过将 CachingConnectionFactory 属性 publisherConfirmType 设置为 ConfirmType.CORRELATED 和 publisherReturns 来支持确认(具有相关性)和返回的消息 > 属性为“真”。
- 设置这些选项后,工厂创建的 Channel 实例被包装在 PublisherCallbackChannel 中,用于促进回调。 当获得这样的频道时,客户端可以向 Channel 注册一个 PublisherCallbackChannel.Listener。 PublisherCallbackChannel 实现包含将确认或返回路由到适当侦听器的逻辑。
- 连接和通道监听器
- 连接工厂支持注册 ConnectionListener 和 ChannelListener 实现。 这允许您接收连接和通道相关事件的通知。
AmqpTemplate
- 目前只有一个实现:RabbitTemplate。
- 可以引入spring-retry实现重试功能。使用Java的@Configuration注册如下:
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500);
backOffPolicy.setMultiplier(10.0);
backOffPolicy.setMaxInterval(10000);
retryTemplate.setBackOffPolicy(backOffPolicy);
template.setRetryTemplate(retryTemplate);
return template;
}
- AmqpTemplate 的 RabbitTemplate 实现支持发布者确认和返回。对于返回的消息,模板的 mandatory 属性必须设置为 true 或 mandatory-expression 必须评估为 true 对于特定消息。 此功能需要一个 CachingConnectionFactory,其 publisherReturns 属性设置为 true。 通过调用 setReturnsCallback(ReturnsCallback callback) 注册 RabbitTemplate.ReturnsCallback,将返回发送到客户端。
- 关于发送和接收消息的方法参考:RabbitTemplate.java
- 发送相关的send/convertAndSend
- 接收相关的receive/receiveAndConvert
- 批量发布:可以使用BatchingRabbitTemplate.java,可以根据批处理策略进行批量发布消息。存在数据丢失风险!
- 消息接收:
- 轮询消费者
- AmqpTemplate 本身可用于轮询 Message 接收。 默认情况下,如果没有可用消息,则立即返回 null。 没有阻塞。 从版本 1.5 开始,您可以设置 receiveTimeout,以毫秒为单位,并且接收方法阻塞长达那么长时间,等待消息。 小于零的值意味着无限期地阻塞(或至少直到与代理的连接丢失)。
- 相关函数:receive/receiveAndConvert
- 异步消费者:通过@RabbitListener注解注册监听器,预期默认值为250.
- 批处理:
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setBatchListener(true);
return factory;
}
@RabbitListener(queues = "batch.1")
public void listen1(List<Thing> in) {
...
}
// or
@RabbitListener(queues = "batch.2")
public void listen2(List<Message<Thing>> in) {
...
}
@RabbitListener及容器
除了常见的通过RabbitListener注解注册监听器端点,还可以通过编程的方式。代码实例如下所示。
@Configuration
@EnableRabbit
public class AppConfig implements RabbitListenerConfigurer {
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
SimpleRabbitListenerEndpoint endpoint = new SimpleRabbitListenerEndpoint();
endpoint.setQueueNames("anotherQueue");
endpoint.setMessageListener(message -> {
// processing
});
registrar.registerEndpoint(endpoint);
}
}
- 创建监听器容器
- 方式1、使用SimpleRabbitListenerEndpoint,如下代码所示。
@Bean
public SimpleMessageListenerContainer factoryCreatedContainerSimpleListener(
SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory) {
SimpleRabbitListenerEndpoint endpoint = new SimpleRabbitListenerEndpoint();
endpoint.setQueueNames("queue.1");
endpoint.setMessageListener(message -> {
...
});
return rabbitListenerContainerFactory.createListenerContainer(endpoint);
}
- 方式2、创建后添加侦听器
@Bean
public SimpleMessageListenerContainer factoryCreatedContainerNoListener(
SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory) {
SimpleMessageListenerContainer container = rabbitListenerContainerFactory.createListenerContainer();
container.setMessageListener(message -> {
...
});
container.setQueueNames("test.no.listener.yet");
return container;
}
【注意】这两种方式创建的容器只是一个普通的bean,并没有注册到RabbitListenerEndpointRegistry中。
- SMLC(SimpleMessageListenerContainer)有以下特性,但DMLC(DirectMessageListener- Container)没有:
- batchSize:使用SMLC,您可以设置此值以控制在一个事务中传递的消息数量或减少ack的数量,但它可能会导致失败后重复传递的数量增加。(DMLC有messagesPerAck,你可以使用它来减少ack,与batchSize和SMLC一样,但它不能用于事务——每个消息在一个单独的事务中被交付和ack)。
- consumerBatchEnabled:允许在消费者中对离散消息进行批处理。
- maxConcurrentConsumers和consumer伸缩间隔或触发器——DMLC中没有自动伸缩功能。但是,它允许您以编程方式更改consumersPerQueue属性,并相应地调整消费者。
- 与SMLC相比,DMLC有以下优点:
- 在运行时添加和删除队列更有效。使用SMLC,将重启整个使用者线程(取消并重新创建所有使用者)。通过DMLC,未受影响的消费者不会被取消。
- 避免了RabbitMQ Client线程和consumer线程之间的上下文切换。
- 线程在使用者之间共享,而不是在SMLC中为每个使用者拥有一个专用线程。
- 检测空闲消费者:
- 可通过配置SimpleMessageListenerContainer或SimpleRabbitListenerContainerFactory的idleEventInterval属性。
- 可通过使用@EventListener监听特定的消费者
public class Listener {
@RabbitListener(id="someId", queues="#{queue.name}")
public String listen(String foo) {
return foo.toUpperCase();
}
@EventListener(condition = "event.listenerId == 'someId'")
public void onApplicationEvent(ListenerContainerIdleEvent event) {
...
}
}