ActiveMQ笔记66-高级特性之消费重试机制

具体哪些情况会引起消息重发?

  1. Client用了事务且在session中调用了rollback()
  2. Client用了事务且在调用commit()前关闭或者没有commit()
  3. 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(死信队列)。

扫描二维码关注公众号,回复: 9202931 查看本文章

如果希望修改重发策略,也就是用自定义值覆盖默认值,使用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>
发布了172 篇原创文章 · 获赞 1 · 访问量 7147

猜你喜欢

转载自blog.csdn.net/qq_36059561/article/details/104270551