RabbitMQ 高级特性

1.1 消息的可靠投递

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

    1. confirm 确认模式
    2. return  退回模式

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

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

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

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

     //测试   Confirm 模式
    @Test
    public void testConfirm() {

         //定义回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 相关配置信息
             * @param ack   exchange交换机 是否成功收到了消息。true 成功,false代表失败
             * @param cause 失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                //System.out.println("confirm方法被执行了...."+correlationData.getId());

                 //ack 为  true表示 消息已经到达交换机
                if (ack) {
                    //接收成功
                    System.out.println("接收成功消息" + cause);
                } else {
                    //接收失败
                    System.out.println("接收失败消息" + cause);
                    //做一些处理,让消息再次发送。
                }
            }
        });

        //进行消息发送
        for (int i = 0; i < 5; i++) {
            rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message Confirm...");

        }
   //进行睡眠操作
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //测试 return模式
    @Test
    public void testReturn() {

        //设置交换机处理失败消息的模式   为true的时候,消息达到不了 队列时,会将消息重新返回给生产者
        rabbitTemplate.setMandatory(true);

        //定义回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * @param message   消息对象
             * @param replyCode 错误码
             * @param replyText 错误信息
             * @param exchange  交换机
             * @param routingKey 路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return 执行了....");

                System.out.println("message:"+message);
                System.out.println("replyCode:"+replyCode);
                System.out.println("replyText:"+replyText);
                System.out.println("exchange:"+exchange);
                System.out.println("routingKey:"+routingKey);

                //处理
            }
        });
        //进行消息发送
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message return...");

        //进行睡眠操作
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

1.1 消息的可靠投递小结

使用rabbitTemplate .setConfirmCallback 设置回调函数。当消息发送到 exchange 后回调 confirm 方法。在方法中判断ack,如果为 true ,则发送成功,如果为 false ,则发送失败,需要处理。
使用rabbitTemplate.setReturnCallback 设置退回函数,当消息从 exchange 路由到 queue 失败后,如果设置了rabbitTemplate.setMandatory(true)参数,则会将消息退回给 producer 。并执行回调函数returnedMessage。

1.2 Consumer Ack

ack指Acknowledge,确认。 表示消费端收到消息后的确认方式。

有三种确认方式:

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

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

@Component
public class AckListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //1、获取消息的id
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {

        //2、获取消息
        System.out.println("message:"+new String(message.getBody()));

        //3、进行业务处理
        System.out.println("=====进行业务处理====");

        //模拟出现异常
        //int  i = 5/0;

        //4、进行消息签收 deliveryTag 消息id   是否批量.true:将一次性ack所有小于deliveryTag的消息 
            //channel.basicAck(deliveryTag, false);
            System.out.println("收到了消息:"+deliveryTag);
        } catch (Exception e) {

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

        }
    }
}

1.2 Consumer Ack 小结

如果在消费端没有出现异常,则调用channel.basicAck(deliveryTag,false);方法确认签收消息
如果出现异常,则在 catch 中调用 basicNack或 basicReject,拒绝消息,让 MQ 重新发送消息。

1.2 消息可靠性总结

1. 久化
exchange要持久化
queue要持久化
message要持久化
2. 生产方确认 Confirm
3. 消费方确认 Ack
4. Broker 高可用

1.3 TTL

TTL 全称 Time To Live (存活 时间 / 过期时间 )。
当消息到达存活时间后,还没有被消费,会被自动清除。
RabbitMQ 可以对消息设置 过期时间 ,也可以对整个队列( Queue )设置 过期时间
RabbitMQ 高级特性_第1张图片

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

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

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

1.4 死信队列

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

RabbitMQ 高级特性_第2张图片

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

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

2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false

3. 原队列存在消息过期设置,消息到达超时时间未被消费;

RabbitMQ 高级特性_第3张图片

 

1.4 死信队列小结

1. 死信交换机和死信队列和普通的没有区别

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

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

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

2. 消费者拒接消费消息,并且不重回队列;

3. 原队列存在消息过期设置,消息到达超时时间未被消费

 1.6 延迟队列

很可惜,在RabbitMQ中并未提供延迟队列功能。

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

RabbitMQ 高级特性_第4张图片

 

延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。

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

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

实现方式:

1. 定时器

2. 延迟队列

RabbitMQ 高级特性_第5张图片

 1.5 延迟队列小结

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

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

1.6 消息幂等性保障

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

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

消息幂等性保障--乐观锁机制

RabbitMQ 高级特性_第6张图片

 

1.7 消息积压

-消费者宕机积压

-消费者消费能力不足积压

-发送者发流量太大

解决方案:上线更多的消费者,进行正常消费上线专门的队列消费服务,将消息先批量取出来,记录数据库,再慢慢处理

你可能感兴趣的:(rabbitmq,java,分布式)