ActiveMQ笔记66-高级特性之消费重试机制
原创
©著作权归作者所有:来自51CTO博客作者qq59ce45caba461的原创作品,请联系作者获取转载授权,否则将追究法律责任
具体哪些情况会引起消息重发?
- Client用了事务且在session中调用了rollback()
- Client用了事务且在调用commit()前关闭或者没有commit()
- Client在CLIENT_ACKNOWLEDGE的传递模式下,在session中调用了recover()
消息重发的间隔和重发次数是多少?
消息重发的间隔是每秒钟重发6次。
对有毒消息Poison ACK的理解。
一个消息被redelivedred超过默认的最大重发次数(默认6次)时,消费端会给MQ发送一个poison ack表示这条消息有毒,告诉broker不要再发了。这时候broker会把这条消息放到死信队列中。
ActiveMQ官网对重试机制的解释:http://activemq.apache.org/redelivery-policy
initialRedeliveryDelay和maximumRedeliveries是最常用的两个参数。
Property
| Default Value
| Description
|
backOffMultiplier
| 5
| 重连时间间隔递增倍数,只有大于1和启用useExponentialBackOff参数时才生效。
|
collisionAvoidanceFactor
| 0.15
| 设置防止冲突范围的正负百分比,只有启用useCollisionAvoidance参数时,才生效,在延迟时间上再加一个时间的波动范围。
|
initialRedeliveryDelay
| 1000L
| 初始重发延迟时间。
|
maximumRedeliveries
| 6
| 最大重试次数,达到最大重连次数后抛出异常,-1表示不限制次数,0表示不进行重传。
|
maximumRedeliveryDelay
| -1
| 最大传送延迟,只在useExponentialBackOff为true时有效。假设重连间隔10ms,倍数为2,第二次重连间隔为20ms,第三次重连间隔是40ms,当重连最大时间间隔达到直达重连时间间隔时,以后每次重连时间间隔都为最大重连时间间隔。
|
redeliveryDelay
| 1000L
| 重发延迟时间,当initialRedeliveryDelay=0时生效。
|
useCollisionAvoidance
| false
| 启用防止冲突功能。
|
useExponentialBackOff
| false
| 启用指数倍数递增的方式增加延迟时间。
|
创建JmsProducer_Redelivery.java和JmsConsumer_Redelivery.java。对于consumer,开启事务,并注释掉commit(),故意不执行commit()。
package com.wsy.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ScheduledMessage;
import javax.jms.*;
public class JmsProducer_Redelivery {
public static final String ACTIVEMQ_URL = "tcp://192.168.0.123:61616";
public static final String QUEUE_NAME = "Redelivery";
public static void main(String[] args) throws JMSException {
// 创建连接工厂,按照给定的url地址采用默认的用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂,获取Connection并启动
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
// 创建Session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建目的地(目的地有两个子接口,分别是Queue和Topic)
Queue queue = session.createQueue(QUEUE_NAME);
// 创建消息生产者,生产的消息放到queue中
MessageProducer messageProducer = session.createProducer(queue);
// 使用messageProducer生产消息发送到队列中
for (int i = 0; i < 3; i++) {
// 创建一条消息,可以理解成字符串
TextMessage textMessage = session.createTextMessage("message-" + i);
messageProducer.send(textMessage);
}
// 按照资源打开的相反顺序关闭资源
messageProducer.close();
session.close();
connection.close();
}
}
package com.wsy.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsConsumer_Redelivery {
public static final String ACTIVEMQ_URL = "tcp://192.168.0.123:61616";
public static final String QUEUE_NAME = "Redelivery";
public static void main(String[] args) throws JMSException {
// 创建连接工厂,按照给定的url地址采用默认的用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂,获取Connection并启动
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
// 创建Session
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
// 创建目的地(目的地有两个子接口,分别是Queue和Topic)
Queue queue = session.createQueue(QUEUE_NAME);
// 创建消费者,指明从queue取消息
MessageConsumer messageConsumer = session.createConsumer(queue);
while (true) {
// 因为向队列中存放的是TextMessage的实例,所以取出来的时候,也要用TextMessage的实例来接收
// 这里的receive()方法表示一直等待,如果给它传一个long类型的毫秒数,表示consumer等待超时时间
TextMessage textMessage = (TextMessage) messageConsumer.receive(1000L);
if (textMessage != null) {
System.out.println("消费者消费:"+textMessage.getText());
} else {
break;
}
}
// 上面开启了事务,正常情况下,必须执行commit();这里模拟出问题的情况,注释掉commit();
// session.commit();
// 按照资源打开的相反顺序关闭资源
messageConsumer.close();
session.close();
connection.close();
}
}
运行Producer,运行Consumer。如果程序正确的情况下,这一次消息的发送和消费就完成了。因为consumer里没有执行commit,对于broker来说,它没有收到consumer的反馈,它认为consumer并没有消费掉消息。
此时,再启动consumer,会触发重发机制,consumer依旧会消费到消息,但是不告诉broker。再启动consumer6次,发现第6次收不到消息了,意味着broker已经把消息放到死信队列里了。也就是在第7次重试的时候,收不到消息了。
此时,打开管理页面的queue标签,会发现多了一个DLQ(死信队列)。
如果希望修改重发策略,也就是用自定义值覆盖默认值,使用RedeliveryPolicy这个类的实例,修改实例的属性值,最后再将实例设置到ActiveMQConnectionFactory实例的RedeliveryPolicy属性上即可,这里以修改重试次数为例,其他属性的修改方法类似,具体看代码。
// 手动修改RedeliveryPolicy(重发策略)
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(3);// 修改重发次数为3次
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);// 将重发策略设置到ConnectionFactory中
如果项目是和Spring整合的,可以将这些属性值设置到Spring的配置文件中。
<!--定义RedeliveryPolicy(重发机制)-->
<bean id="activeMQRedeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<property name="useCollisionAvoidance" value="false"/>
<property name="useExponentialBackOff" value="true"/>
<property name="maximumRedeliveries" value="3"/>
<property name="initialRedeliveryDelay" value="1000"/>
<property name="backOffMultiplier" value="2"/>
<property name="maximumRedeliveryDelay" value="1000"/>
</bean>
<!--创建连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
<!--引用自定义重发机制-->
<property name="redeliveryPolicy" ref="activeMQRedeliveryPolicy"/>
</bean>