RabbitMQ如何保证消息的可靠性

1.生产者没有成功把消息发送到MQ

丢失原因:因为网络传输的不稳定性,当生产者在向mq发送消息的过程中,mq没有成功收到消息,但是生产者却以为mq成功收到消息,没有再次重复发送该消息,从而导致消息的丢失。

解决办法:有两个解决办法:

1.事务机制;

2.confirm机制,最常用的是confirm机制;

注意:RabbitMQ的事务机制是同步的,很耗型能,会降低RabbitMQ的吞吐量。confirm机制是异步的,生成者发送完一个消息之后,不需要等待RabbitMQ的回调,就可以发送下一个消息,当RabbitMQ成功接收到消息之后会自动异步的回调生产者的一个接口返回成功与否的消息

1.1、事务机制

AMQP实现了事务机制,类似mysql的事务。

事务机制三个方法:

txSelect:用于将当前changel设置成transation模式。

txCommit:用于提交事务。

txRollback:回滚事务。

通过txSelect开启事务后,便可以发送消息给RabbitMQ服务器,通过

txCommit提交成功,如果提交之前出现了异常,就txRollback回滚。

问题:开启-提交-回滚,每次提交,都相当于一次请求,降低了消息的吞吐量,

因为走的通信太多,大量消息就会大量请求服务器,很耗时。

1.2、confirm机制

        RabbitMQ可以开启 confirm 模式,在生产者那里设置开启 confirm 模式之后,生产者每次写的消息都会分配一个唯一的 id,如果消息成功写入 RabbitMQ 中,RabbitMQ 会给生产者回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,生产者可以重新发送。

    /**
     * 线程安全有序的一个哈希表,适用于高并发的情况
     * 1.轻松的将序号与消息进行关联
     * 2.轻松批量删除条目 只要给到序列号
     * 3.支持并发访问
     */
    ConcurrentSkipListMap<Long, String> outstandingConfirms = new ConcurrentSkipListMap<>();
  
    /**
     * 消息确认成功的回调函数,方法参数:1.消息序列号、2.批量标识,true为批量,false 确认当前序列号消息
     */
    ConfirmCallback ackCallback = (deliveryTag, multiple) -> {
        if (multiple) {
            //2:删除到已经确认的消息 剩下的就是未确认的消息
            ConcurrentNavigableMap<Long, String> confirmed = outstandingConfirms.headMap(deliveryTag, true);
            //删除
            confirmed.clear();
        }else{
            //只清除当前序列号的消息
            outstandingConfirms.remove(deliveryTag);
        }
        System.out.println("确认的消息:"+ deliveryTag);
    };
  
    //消息确认失败的回调,方法参数:1.消息的标记 2.是否为批量确认
    ConfirmCallback nackCallback = (deliveryTag, multiple) -> {
        //3:打印一下未确认的消息都有哪些
        String message = outstandingConfirms.get(deliveryTag);
        System.out.println("发布的消息:"+message+"未被确认,序列号:"+deliveryTag);
    };
  
    //添加一个异步确认的监听器 ,方法参数:1.确认收到消息的回调、2、未收到消息的回调
    //异步通知
    channel.addConfirmListener(ackCallback, nackCallback);
  

2、RabbitMQ接收到消息之后丢失了消息

 丢失原因:mq收到消息之后,是先存在内存之中,那么如果不持久化,可能会由于mq重启或者宕机,导致消息丢失。所以必须要持久化

解决办法 :开启RabbitMQ的持久化。当生产者把消息成功写入RabbitMQ之后,RabbitMQ就把消息持久化到磁盘。结合上面的说到的confirm机制,如果开启了消息的持久化,只有当消息成功持久化磁盘之后,才会回调生产者的接口返回ack消息,否则都算失败 。存入磁盘的消息不会丢失,就算RabbitMQ挂掉了,重启之后,他会读取磁盘中的消息,不会导致消息的丢失。

极端情况:mq还没有进行持久化,就挂掉了。或者由于网络原因,生产者收不到confirm。我们对于极其重要的消息就做数据入库。

例如:生产者要发送一条消息之前,先插入数据库,设置状态status表示设置为0,。收到回调成功后设置为1。通过定时任务来定期扫描重发,进行消息的补偿。要设置最大重试次数,防止不断的重发消息消耗数据库的性能,消耗定时任务和内存资源等。这种方式会加剧数据库的交互压力,因为每发送一条消息都要去改变数据库的状态爱,建议搞一个单独的库,处理这样非常重要的消息。也可以用redis缓存做状态的变更,来减少数据库的压力,具体视情况而定吧!


1.3、消费者弄丢了消息

丢失的原因 :如果RabbitMQ成功的把消息发送给了消费者,那么RabbitMQ的ack机制会自动的返回成功,表明发送消息成功,下次就不会发送这个消息。但如果就在此时,消费者还没处理完该消息,然后宕机了,那么这个消息就丢失了。

解决的办法 :简单来说,就是必须关闭 RabbitMQ 的自动 ack,采用手动ack (消息应答机制)。这样的话,如果你还没处理完,不就没有 ack了! 那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。RabbitMQ消息丢失之后的处理:消息自动重新入队。

消息应答机制 : 为了保证消息在消费过程中不丢失,rabbitmq 引入消息应答机制,消息应答就是:消费者在接收到消息并且处理该消息之后,告诉 rabbitmq 它已经处理了,rabbitmq 可以把该消息删除了。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值