RabbitMQ消息可靠性(消息丢失解决方法)

消息可靠性涉及方面:

1、发送端确定机制

2、持久化存储机制:队列持久化、交换器持久化、消息持久化

3、消费端Ack机制

4、消费端限流

消息发送端确定机制:

以springboot进行实例演示,原生API自行查找了解,springboot整合参考:https://blog.csdn.net/qq_28326501/article/details/115509447

消息发送端只需修改配置项和controller即可。其余不涉及修改。

修改配置项:

  添加配置项

        spring.rabbitmq.publisher-confirm-type=correlated

        spring.rabbitmq.publisher-returns=true

发送Controller类

package com.cc.rabbitmq.controller;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.UnsupportedEncodingException;

@RestController
public class RabbitBizController {

    private RabbitTemplate rabbitTemplate;

    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
        //correlationData:消息体
        //flag:是否确认
        //cause:信息
        this.rabbitTemplate.setConfirmCallback((correlationData, flag, cause) -> {
            if(flag){
                try {
                    System.out.println("确认消息"+correlationData.getId()
                            + new String(correlationData.getReturnedMessage().getBody(), "utf-8")  );
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println(cause);
            }
        });
    }


    @RequestMapping("/sendBiz")
    public String sendMessage() throws UnsupportedEncodingException {

        MessageProperties props = new MessageProperties();
        props.setCorrelationId("1234");
        props.setConsumerTag("ccmsg");
        //消息类型
        props.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        props.setContentEncoding("utf-8");

        CorrelationData cd = new CorrelationData();
        cd.setId("ccmsg");
        cd.setReturnedMessage(new Message("这是msg1的响应".getBytes("utf-8"), null));

        Message message = new Message("这是等待确认的消息".getBytes("utf-8"), props);

        rabbitTemplate.convertAndSend("my.ex", "direct.biz2.ex", message, cd);
        return "ok";
    }
}

示例:

使用浏览器请求

发送端:

消费端:

 

持久化存储机制:

持久化是提高RabbitMQ可靠性的基础,否则当RabbitMQ遇到异常时(如:重启、断电、停机 等)数据将会丢失。主要从以下几个方面来保障消息的持久性:

1、交换器Exchange的持久化。通过定义时设置durable 参数为ture来保证Exchange相关的元数据不不丢失。

2、队列Queue的持久化。通过定义时设置durable 参数为ture来保证Queue相关的元数据不不丢失。

3、消息的持久化。通过将消息的投递模式 (BasicProperties 中的 deliveryMode 属性)设置为 2 即可实现消息的持久化,保证消息自身不丢失。

示例:

交换器持久化设置:

队列持久化设置:

 

消费端Ack机制:

abbitMQ在消费端会有Ack机制,即消费端消费消息后需要发送Ack确认报文给Broker端,告知自 己是否已消费完成,否则可能会一直重发消息直到消息过期(AUTO模式)。

一般而言,我们有如下处理手段:

1. 采用NONE模式,消费的过程中自行捕获异常,引发异常后直接记录日志并落到异常恢复表, 再通过后台定时任务扫描异常恢复表尝试做重试动作。如果业务不自行处理则有丢失数据的风险

2. 采用AUTO(自动Ack)模式,不主动捕获异常,当消费过程中出现异常时会将消息放回 Queue中,然后消息会被重新分配到其他消费者节点(如果没有则还是选择当前节点)重新被消费,默认会一直重发消息并直到消费完成返回Ack或者一直到过期

3. 采用MANUAL(手动Ack)模式,消费者自行控制流程并手动调用channel相关的方法返回 Ack

修改配置文件:

#最大重试次数
spring.rabbitmq.listener.simple.retry.max-attempts=5
#是否开启消费者重试(为false时关闭消费者重试,意思不是“不重试”,而是一直收到消息直到jack确认或者一直到超时)
spring.rabbitmq.listener.simple.retry.enabled=true
#重试间隔时间(单位毫秒)
spring.rabbitmq.listener.simple.retry.initial-interval=5000
# 重试超过最大次数后是否拒绝
spring.rabbitmq.listener.simple.default-requeue-rejected=false
#ack模式
spring.rabbitmq.listener.simple.acknowledge-mode=manual

 

处理类:

package com.cc.rabbitmq_consumer.consumer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Random;

@Component
public class MyMessageListener {

    private Random random = new Random();

    /**
     * NONE模式,则只要收到消息后就立即确认(消息出列,标记已消费),有丢失数据的风险
     * AUTO模式,看情况确认,如果此时消费者抛出异常则消息会返回到队列中
     * MANUAL模式,需要显式的调用当前channel的basicAck方法
     * @param message
     */
    @RabbitListener(queues = "my.queue3", ackMode = "MANUAL")
    public void getMessage3(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, @Payload String message){
        System.out.println("消息内容:"+message);
        try {
            //随机获取一个数,随机模拟处理失败
            if (random.nextInt(10) % 3 != 0){
                // 手动nack,告诉broker消费者处理失败,最后一个参数表示是否需要将消息重新入列列
                // channel.basicNack(deliveryTag, false, true);
                // 手动拒绝消息。第二个参数表示是否重新入列
                    channel.basicReject(deliveryTag, true);
            }else {
                // 手动ack,deliveryTag表示消息的唯一标志,multiple表示是否是批量确认
                channel.basicAck(deliveryTag, false);
                System.err.println("已确认消息:" + message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cc_南柯一梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值