RabbitMQ高级特性

目录

1.消息可靠投递

1.1生产方确认模式

1.2生产方退回模式

2.Consumer ACK

3.消费端限流

4.TTL

4.1消息过期时间

4.2队列过期时间

5.死信队列

6.延迟队列

7.消息追踪,日志监控

8.消息可靠性保障

8.1消息补偿机制

8.2.消息幂等性保障

9.集群搭建


1.消息可靠投递

在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。

  1. lconfirm 确认模式
  2. lreturn  退回模式

rabbitmq 整个消息投递的路径为:

producer--->rabbitmq broker--->exchange--->queue--->consumer

  1. 消息从 producer 到 exchange 则会返回一个 confirmCallback 。
  2. 消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。

我们将利用这两个 callback 控制消息的可靠性投递

1.1生产方确认模式

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/spring/spring-rabbitmq.xml")
public class ProducerTest {
    @Autowired
    private RabbitTemplate rabbitTemplatel;

    /**
     * 确认模式
     *  1.确认模式开启 connectionFactory publisher-confirms="true"
     *  2.定义一个confirms 回调函数
     */
    @Test
    public void testHelloWord(){

        //定义回调
        rabbitTemplatel.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 配置信息
             * @param b 交换机是否收到了消息, true success
             * @param s 失败原因
             */
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if(b){
                    System.out.println("接受成功消息");
                }else{
                    System.out.println("接受消息失败:" +s);
                }
                System.out.println("回调函数被执行了");
            }
        });
        rabbitTemplatel.convertAndSend("test_confirm_exchange","spring","message test_confirm_exchange");
    }
}

1.2生产方退回模式

  /**
     * 回退模式:当交换机路由到队列失败后
     *  1.回退模式开启 connectionFactory  publisher-returns="true"
     *  2.定义一个return 回调函数
     *  3.设置一个交换机处理消息的模式
     *      1.消息丢弃
     *      2.返回消息发送方
     */
    @Test
    public void testReturn(){
        //设置交换机处理失败模式
        rabbitTemplatel.setMandatory(true);
        //定义回调
        rabbitTemplatel.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * @param message 消息对象
             * @param i 错误码
             * @param s 错误信息
             * @param s1 交换机
             * @param s2 路由键
             */
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("return 执行了");
            }
        });
        rabbitTemplatel.convertAndSend("test_confirm_exchange","spring","message test_confirm_exchange");
    }

2.Consumer ACK

ACK 消费端收到消息的确认方式

  • 自动确认:acknowledge="none"
  • 手动确认:acknowledge="manual"
  • 根据异常情况确认:acknowledge="auto"

其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。


import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

/**
 * Consumer ACK机制:
 *  1. 设置手动签收。acknowledge="manual"
 *  2. 让监听器类实现ChannelAwareMessageListener接口
 *  3. 如果消息成功处理,则调用channel的 basicAck()签收
 *  4. 如果消息处理失败,则调用channel的basicNack()拒绝签收,broker重新发送给consumer
 *
 *
 */

@Component
public class AckListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {
            //1.接收转换消息
            System.out.println(new String(message.getBody()));
            //2. 处理业务逻辑
            System.out.println("处理业务逻辑...");
            //int i = 3/0;//出现错误
            //3. 手动签收
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            //e.printStackTrace();

            //4.拒绝签收
            /*
            第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
             */
            channel.basicNack(deliveryTag,true,true);
            //channel.basicReject(deliveryTag,true);
        }
    }
}

3.消费端限流

package cn.bufanli.rabbit.listener;


import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

/**
 *  consumer 限流
 *  1.确保ack机制为手动确认
 *  2.配置属性 listener-container prefetch="1"
 *   perfetch = 1,表示消费端每次从mq拉去一条消息来消费,直到手动确认消费完毕后,才会继续拉去下一条消息。
 *
 */

@Component
public class QosListener implements ChannelAwareMessageListener{
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        Thread.sleep(1000);
        //1.获取消息
        System.out.println(new String(message.getBody()));
        //2. 处理业务逻辑
        //3. 签收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);

    }

}

4.TTL


 

全称 Time To Live(存活时间/过期时间)当消息到达存活时间后,还没有被消费,会被自动清除

RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间

设置队列过期时间使用参数:x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期

设置消息过期时间使用参数:expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期。

如果两者都进行了设置,以时间短的为准。

4.1消息过期时间

 //消息处理对象,设置一些消息的参数消息
         MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
             public Message postProcessMessage(Message message) throws AmqpException {
                 //1.设置message的消息过期时间
                 message.getMessageProperties().setExpiration("5000");
                 return message;
             }
         };
         rabbitTemplatel.convertAndSend("test_exchange_ttl"
                 ,"ttl.hehe","message test_ttl_exchange" ,messagePostProcessor);

 

4.2队列过期时间

    <!--ttl-->
    <rabbit:queue name="test_queue_ttl" id="test_queue_ttl">
        <!--设置q的参数-->
        <rabbit:queue-arguments>
            <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"></entry>
        </rabbit:queue-arguments>
    </rabbit:queue>

5.死信队列

死信队列,英文缩写:DLX  。Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX

消息成为死信的三种情况:

1. 队列消息长度到达限制
2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列 ,requeue=false
3. 原队列存在消息过期设置,消息到达超时时间未被消费

 

队列绑定死信交换机:

给队列设置参数 : x-dead-letter-exchange 和 x-dead-letter-routing-key
 
 <!--死信队列
        1.声明正常的队列(test_queue_dlx) 和 交换机(test_exchange_dlx)
        2.声明死信队列 (queue_dlx)和交换机(test_exchange_dlx)
        3.正常队列绑定死信交换机
            设置两个参数
                x-dead-letter-exchange 队列交换机名称
                x-dead-letter-routing-key 死信交换机使用的routingkey
        4.设置过期时间、最大长度                
        -->
    <rabbit:queue name="test_queue_dlx" id="test_queue_dlx">
        <!--正常队列绑定死信交换机-->
        <rabbit:queue-arguments>
            <!--队列交换机名称-->
            <entry key="x-dead-letter-exchange" value="exchange_dlx" />
            <entry key="x-dead-letter-routing-key" value="dlx.xx" />
            <!--设置队列的过期时间 毫秒 Integer类型-->
            <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
            <!--设置队列的最大长度 条 Integer类型-->
            <entry key="x-max-length" value="10" value-type="java.lang.Integer"/>
        </rabbit:queue-arguments>
    </rabbit:queue>
    <rabbit:topic-exchange name="test_exchange_dlx">
        <rabbit:bindings>
            <rabbit:binding pattern="test.dlx.#" queue="test_queue_dlx"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>
    <!--2.声明死信队列 (queue_dlx)和交换机(test_exchange_dlx)-->
    <rabbit:queue name="queue_dlx" id="queue_dlx">

    </rabbit:queue>
    <rabbit:topic-exchange name="exchange_dlx">
        <rabbit:bindings>
            <rabbit:binding pattern="dlx.#" queue="queue_dlx"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>
1. 死信交换机和死信队列和普通的没有区别


2. 当消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列


3. 消息成为死信的三种情况:

    1. 队列消息长度到达限制;

    2. 消费者拒接消费消息,并且不重回队列;
    
    3. 原队列存在消息过期设置,消息到达超时时间未被消费;

6.延迟队列

1. 延迟队列 指消息进入队列后,可以被延迟一定时间,再进行消费。

2. RabbitMQ没有提供延迟队列功能,但是可以使用 : TTL + DLX 来实现延迟队列效果。

 

需求:

1. 下单后,30分钟未支付,取消订单,回滚库存。

2. 新用户注册成功7天后,发送短信问候。

实现方式:

1. 定时器

2. 延迟队列

RabbitMQ中可以使用:TTL+死信队列 组合实现延迟队列的效果。

 

 

7.消息追踪,日志监控

在RabbitMQ中可以使用Firehose和rabbitmq_tracing插件功能来实现消息追踪。

在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况。对于RabbitMQ而言,可能是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;也有可能是因为交换器与队列之间不同的转发策略;甚至是交换器并没有与任何队列进行绑定,生产者又不感知或者没有采取相应的措施;另外RabbitMQ本身的集群策略也可能导致消息的丢失。这个时候就需要有一个较好的机制跟踪记录消息的投递过程,以此协助开发和运维人员进行问题的定位。

 

firehose的机制是将生产者投递给rabbitmq的消息,rabbitmq投递给消费者的消息按照指定的格式发送到默认的exchange上。这个默认的exchange的名称为amq.rabbitmq.trace,它是一个topic类型的exchange。发送到这个exchange上的消息的routing key为 publish.exchangename 和 deliver.queuename。其中exchangename和queuename为实际exchange和queue的名称,分别对应生产者投递到exchange的消息,和消费者从queue上获取的消息。

注意:打开 trace 会影响消息写入功能,适当打开后请关闭。

  • rabbitmqctl trace_on:开启Firehose命令
  • rabbitmqctl trace_off:关闭Firehose命令

 

rabbitmq_tracing和Firehose在实现上如出一辙,只不过rabbitmq_tracing的方式比Firehose多了一层GUI的包装,更容易使用和管理。

启用插件:rabbitmq-plugins enable rabbitmq_tracing

设置-》#号符号会把所有的的消息都存日志 add trace 即可

添加日志插件

 

8.消息可靠性保障

8.1消息补偿机制

 

8.2.消息幂等性保障

 

幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

 

MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果

9.集群搭建

待续

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值