ActiveMQ(二)SpringBoot整合-消息的手动签收、死信处理
ActiveMQ(二)SpringBoot整合-消息的手动签收、死信处理
前言
通过ActiveMQ(一)入门现在我们已经实现了一个基本可以解决所有业务需求的消息队列。但是目前还有一些需要完善的工作,比如消息的重发机制,对“死信”的管理和处理等
消息重发
消息的重发规则是可以配置的,我们在customer的ActiveMqConfig中配置如下bean
/**
*消息的重发规则配置
*/
@Bean
public RedeliveryPolicy redeliveryPolicy() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
// 是否在每次尝试重新发送失败后,增长这个等待时间
redeliveryPolicy.setUseExponentialBackOff(true);
// 重发次数
redeliveryPolicy.setMaximumRedeliveries(5);
// 重发时间间隔,默认为1000ms(1秒)
redeliveryPolicy.setInitialRedeliveryDelay(1000);
// 重发时长递增的时间倍数2
redeliveryPolicy.setBackOffMultiplier(2);
// 是否避免消息碰撞
redeliveryPolicy.setUseCollisionAvoidance(false);
// 设置重发最大拖延时间-1表示无延迟限制
redeliveryPolicy.setMaximumRedeliveryDelay(-1);
return redeliveryPolicy;
}
在listener中加入该策略(示例jmsListenerContainerQueue)
//Queue模式连接注入
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerQueue(ActiveMQConnectionFactory connectionFactory){
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
// 关闭Session事务,手动确认与事务冲突
bean.setSessionTransacted(false);
// 设置消息的签收模式(自己签收)
bean.setSessionAcknowledgeMode(INDIVIDUAL_ACKNOWLEDGE);
// 配置消息的重发规则
connectionFactory.setRedeliveryPolicy(redeliveryPolicy());
bean.setConnectionFactory(connectionFactory);
return bean;
}
修改签收逻辑(不手动签收,直接使用session.recover()
触发消息重发,方便测试)
/**
* 不手动签收,创建死信
* @param message
* @param session
*/
@JmsListener(destination = "MyActiveMQQueue", containerFactory = "jmsListenerContainerQueue")
public void receiveQueueMessage(ActiveMQMessage message, Session session) {
int i = 0;
try {
System.out.println("收到消息" + message +":"+ i++);
// 手动签收
// message.acknowledge();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
try {
// 消息重发
session.recover();
} catch (JMSException e1) {
e1.printStackTrace();
}
}
测试
- 启动provider
- 启动consumer
- 发送一条queue消息
- 我们发现消息被发送了6次(重发5次)后被消费,并进入了一个名为ActiveMQ.DLQ的队列
这便是我们提到的“死信”,即超过重发规则限制的消息会统一进入该队列管理。但是所有“死信”都在这里,因此不便于我们对一些“特殊”的“死信”进行单独管理。
死信管理
“死信”管理策略配置:
修改activemq.xml,配置如下策略
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" persistent="true" dataDirectory="${activemq.data}">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" >
<deadLetterStrategy>
<individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true" />
</deadLetterStrategy>
</policyEntry>
<policyEntry topic=">" >
<deadLetterStrategy>
<individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true" />
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
...
</broker>
测试
- 重启activemq
- 启动provider
- 启动consumer
- 发送一条queue消息
- 可以发现,在该策略下,死信进入可辨识的DLQ.MyActiveMQQueue队列,即 DLQ + destination,这样我们就可以针对单独的“死信”队列去处理这些“死信”啦!
“死信”处理
@JmsListener(destination = "DLQ.MyActiveMQQueue", containerFactory = "jmsListenerContainerQueue")
public void receiveDLQDefault(ActiveMQMessage message, Session session) {
System.out.println("处理死信" + message);
try {
message.acknowledge();
} catch (JMSException e) {
e.printStackTrace();
}
}
测试
直接重启customer,发现之前的死信都被成功处理
topic模式消息重发
最后,我们再来测试一下topic模式下的消息重发和“死信”处理
以同样的方法测试后,我们发现topic的“死信”会被收到ActiveMQ.DLQ.topic+destination下,不过无所谓,想要处理的话,方法也如出一辙,不再赘述了。