zoukankan      html  css  js  c++  java
  • 消息队列如何保证消息能百分百成功被消费

    消息队列如何保证消息能百分百成功被消费

      目前常用的消息队列有很多种,如RabbitMQ,ActiveMQ,Kafka...下面以RabbitMQ为例来讲如何保证消息队列中的信息能百分百被消费掉.

      其中消费队列的工作流程如下:
      

      写个伪代码:  

    public boolean sendOrderMessage(String orderMessage){
    RabbitTemplate rb = new RabbitTemplate();

        try{
            rb.send(orderMessage);
        }catch (Exception e){
            return  false;
        }
        return  true;
    

    }
      那么在订单服务发送完消息的情况下,物流服务器突然宕机,或者MQ服务器突然宕机怎么办?发送方已经将消息发送,而消费方却没有接收到任何需要消费的信息,这就会导致,消息消费失败.

    1)消息生产者把消息发送给MQ,如果接收成功,MQ会返回一个ack消息给生产者

    2)如果消息接收不成功,MQ会返回一个nack消息给生产者

      碰到上面的问题,我们首先第一会想到解决方案就是发送方在消息发送到MQ之后,将消息给写到硬盘上,这样即使MQ服务器宕机,也不会导致未消费的消息丢失.

    那么问题又出现了,当高并发场景下这种方式显而易见是不合理的,因为与磁盘进行I/O交互是相对非常慢的.当然这种情况我们可以有解决方案: 可以在缓存中将消息缓存下来,比如当消息满1000条时就将这些消息一次性写到磁盘中.

    这种方案虽然可以解决I/O交互问题.但是还是不能解决宕机出现的问题,假如缓存了900条消息,并且这些消息均为被消费,此时宕机了,同样会导致消息的丢失.显然这种解决方案还需要优化.

      在上面方案的基础上.我们可以再增加一个机制,增加一个确认机制,如图:

    流程解释:

    1)订单服务生产者再投递消息之前,先把消息持久化到Redis或DB中,建议redis,高性能。消息的状态为发送中。

    2)confirm机制监听消息是否发送成功?如ack成功消息,删除redis中此消息。

    3)如果nack不成功的消息,这个可以根据自身的业务选择是否重发此消息。也可以删除此消息,由自己的业务决定。

    4)这边加了个定时任务,来拉取隔一定时间了,消息状态还是为发送中的,这个状态就表明,订单服务是没有收到ack成功消息。

    5)定时任务会作补偿性的投递消息。这个时候如果MQ回调ack成功接收了,再把redis中此消息删除

    这种方案其实就是加上一个补偿机制,不管MQ有没有真正的接收到,只要缓存redis中的消息还是发送中的状态,就意味着这个消息没有成功的投递出去,也没有被消费,定时任务启动时就要重新发送.

    当然定时任务那边我们还可以加上一个补偿的次数,如果大于3次,还是没有收到ack消息,那就直接把消息的状态设置为【失败】,由人工去排查到底是为什么?

    不过这样的方案,就会有可能发送多次相同的消息,很有可能MQ已经收到了消息,就是ack消息回调时出现网络故障,没有让生产者收到。那就要要求消费者一定在消费的时候保障幂等性。

  • 相关阅读:
    【C++】资源管理
    【Shell脚本】逐行处理文本文件
    【算法题】rand5()产生rand7()
    【Shell脚本】字符串处理
    Apple iOS产品硬件参数. 不及格的程序员
    与iPhone的差距! 不及格的程序员
    iPhone游戏 Mr.Karoshi"过劳死"通关. 不及格的程序员
    XCode V4 发布了, 苹果的却是个变态. 不及格的程序员
    何时readonly 字段不是 readonly 的?结果出呼你想象!!! 不及格的程序员
    object file format unrecognized, invalid, or unsuitable Command 不及格的程序员
  • 原文地址:https://www.cnblogs.com/globalcoding/p/12770537.html
Copyright © 2011-2022 走看看