RabbitMQ如何保证消息不丢失

(一)开篇

保证消息不丢失,可以从三个方面来做处理:
(1)生产者方面;
(2)MQ本身;
(3)消费者方面。
今天先从生产者角度开始。
本工程实例的背景是springboot整合rabbitMQ。

(二)信道的 Confirm 确认模式

默认情况下,生产者和 RabbitMQ 服务间创建的信道 Channel 不是 Confirm (确认)模式的。即生产者将消息发送给 RabbitMQ 服务后,就不再关心消息是否真正的到达了 。如果RabbitMQ发生了类似于突然宕机等情况,消息就会丢失。为避免这种情形,我们需要将生产者和 RabbitMQ 服务间创建的信道设置为 Confirm (确认)模式。
(1)一旦信道进入 confirm 模式,所有在信道上发布的消息都会被指派一个唯一的 ID(从 1 开始);
(2)一旦消息被投递给所有匹配的队列后,RabbitMQ 会发送一个确认(Basic.ACK)给生产者(包含消息的唯一 ID)。
(3)如果消息和队列是持久化的,那么确认消息只会在队列将消息写入磁盘后才会发出。
(4)如果 RabbitMQ 发生内部错误从而导致消息丢失,会发送一条 nack(not acknowledged,未确认)消息给生产者。
(5)RabbitMQ 传回给生产者的确认消息中的 deliveryTag 包含了确认消息的序号;通过这个需要可以实现批量发送和确认消息。

(三)定义生产者

定义生产者的bean实现了RabbitTemplate.ConfirmCallback和RabbitTemplate.ReturnCallback接口,这两个接口的作用如下:
ConfirmCallback接口:消息发送到 Broker 后触发回调,确认消息是否到达 Broker 服务器,也就是只确认是否正确到达 Exchange 中。
在配置文件添加配置启用ConfirmCallback:

 spring:
  rabbitmq:
    publisher-confirms: true

ReturnCallback接口:
在配置文件添加配置启用ReturnCallback:

 spring:
  rabbitmq:
    publisher-returns: true

定义生产者代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@ConditionalOnClass(RabbitTemplate.class)
public class RabbitProducerConfiguration implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
   

    protected static final Logger LOGGER = LoggerFactory.getLogger(RabbitProducerConfiguration.class);

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    /**
    * 此处的rabbitTemplate必须是prototype类型
    * (1)rabbitTemplate是thread safe的,主要是channel不能共用,但是在rabbitTemplate源码里
    * channel是放在每次推拉线程的threadlocal中的,所以channel在单例情况下是线程安全的,
    * 不是造成此处必须为prototype类型的原因。
    * (2)但此处的rabbitTemplate必须要设置回调类,不同的生产者需要对应不同的
    * ConfirmCallback,如果rabbitTemplate设置为单例bean,则所有的rabbitTemplate实际的
    * ConfirmCallback为最后一次申明的ConfirmCallback。
    */
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
   
        return new RabbitTemplate(connectionFactory);
    }
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
   
        if (ack) {
   
            LOGGER.info("消息成功到达Exchange");
        }else{
   
            // 根据业务逻辑实现消息补偿机制
            if (correlationData instanceof CorrelationDataExt) {
   
                CorrelationDataExt messageCorrelationData = 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值