RabbitMQ使用时注意的一些问题

 一、前言

      上篇RabbitMQ的博文居然上了推荐,效果很不错,接下来我们就来聊聊我们RabbitMQ的方案,先谈方案,代码等等后面补上,感觉不错给我点点关注,点点👍,本来能早点写完这篇博文的,由于工作最近很繁忙稍微推迟一些时间。

 二、方案

      方案从两方面谈:生产者的投递以及消费端的消费。

      生产端

      生产端要保障的是消息发出去,RabbitMQ的Borker收到并且返会确认收到,由于网络的原因消息发送Borker的时候可能失败,另外Borker返回给生产者确认的时候也可能发生闪断,所以为了保障100%投递我们还需要合理的补偿机制。主要提供两种方案:消息打标和延时队列,还是以最经典的下订单的场景为例:

      消息打标:

      消息打标的意思就是将消息落库以后,里面对消息状态进行记录,标记为发送中以及发送成功两种状态,然后采用定时轮询任务的方式去查找消息是否发送成功,具体看下图:

      

        接下来我们详细介绍一下该流程:

        1.订单生成同时落地到订单表和消息记录表,这里的消息记录表可以用MongoDB或者ES等方式进行替代;

        2.从消息记录表读取消息,发送消息到Borker同时更改消息状态为发送中;

        3.假设消息接收成功,Borker接收消息成功,并返回确认收到给生产者,这个时候更新消息记录表消息状态为成功;

        4.假设消息接收失败,可能失败的地方有两个处:Borker接收消息和返回消息都可能发生网络问题或者其他状况,导致生产者接收不到返回值;

        5.定时任务轮询,规定时间内没有发送成功消息再次按照步骤2进行投递,这个规定时间最多容忍2或者次查询轮询同一个条消息,如果依然接收不成功,那么则设置消息接收失败,这里可能是交换机或者队列绑定失败造成的,需要我们人工排查;

        延迟队列:

        使用延迟队列对消息的延迟投递,通过回调函数做二次检查确认消息投递成功;延迟队列是针对消息来说,是指当消息发送出去以后不想立即被消费,而是等待特定的时间后,消费者才进行消费,比如我们常见的支付付款,30分钟内必须付款成功,否则就异常,这里是延迟队列的经典场景;RabbitMQ来说本身是不支持延迟队列的,但是可以通过死信队列和过期时间来实现延迟队列,简单解释下就是生产队发布消息到正常队列,设置过期时间,绑定死信交换机,消费端直接消费死信队列,这样就完成延迟队列的实现;通过延时队列去实现消息100%可靠投递的化,会涉及到消费者消费的问题,相对比较复杂;

       

       接下来我们详细介绍通过延迟队列实现消息100%可靠投递的流程:

       1.当订单落库以后,生产者发送消息给Borker,并且发送延时消息,该消息是用来做二次检查确认的;

       2.消费者(2)消费成功以后,将消费成功的消息体回发到Borker中(3);

       3.回调服务(4)消费Boker中消费者消费成功的消息体并且入库;

       4.当延时队列(5)中存在消息时候做检查数据库是否存在消息,如果存在着消息消费成功,如果不存在则消息消费失败,同时再次调用生产者发送消息;

       以上就是我前面提到过的两种方案,我们公司现在还是再用第一种 ,对于第二种主要是为了提升系统的吞吐量,减少一次入库;其实合适思想就是通过补偿,来完成消息100%投递,这里面就存在一个问题,同一条消息可能会被投递多次,可能照成消费端多次消费同一条消息,所以这个时候我们就要考虑客户端幂等性的设计;另外消费端消费的时候,消息的顺序是得不到保障的,如果有业务之间相互依赖就需要考虑消息顺序不一致时候处理。

       消费端

       消费端消费需要注意得地方就是上面提到得两种情况:幂等性以及消息的顺序问,接下来我们也来聊一聊消费端的设计问题。

       幂等性

       幂等性这个也是我们Web端设计时候要考虑的问题之一,幂等性就是多次提交与一次提交看到的结果是相同的,这里解释的有点简单,感兴趣的自己百度下深入了解下;这里我们来聊聊我们消费端怎么来设计实现幂等;

       1.唯一id+业务规则,利用数据库主键去重,我们现在就是通过唯一id去重,业务规则具体可以根据你们的使用场景去决定你是否需要加上这个条件;

       2.通过Redis去实现幂等,通过Redis缓存去判断该消息是否消费过,但是这个时候我们要考虑Redis与数据库怎么要保障原子性的问题;

       业务依赖(顺序性)

       这个问题其实我不太介意设计这么复杂,但是要是真是存在这样的场景,那也聊一聊我的一些想法:

       1.保障顺序消费的消息都需要在同一个队列中,保障顺序消费的消息的Id是一致的,并且消费者只能有一个;

       2.增加消息体属性:顺序消费的标记用来区分谁先消费;

       3.当消费端消费以后,如果是消费顺序正确,那么落库,如果消息顺序不正确,则先落库消息,并且发送延时消息;

       4.当收到延时队消息的时候,然后根据消息id查询数据库,进行数据处理,如果还是顺序不对则再次发送延时消息;

       为什么这么设计?其实不这么设计也是可以,比如我们消息顺序不对的时候,直接投入延时队列,这种不能区分到底谁先被消费,只能靠随机去尝试,极端情况下可能很多次都无法保证顺序是一致,所以我增加顺序标记的想法,为什么要保证到同一个队列和只有一个消费者,这是为了保证顺序性,当多个队列,多个消费者的时候顺序性更难保证。

       以上都是我自己的一些想法,如果发现不对,请指正谢谢!

 三、结束

       最近一段时间比较忙,代码部分我想做一些封装,暂时没时间写,等等稳定以后补充上,大家在稍微等等,接下来还会介绍一些消息存储和镜像队列方面的知识,欢迎大家加群438836709!欢迎大家关注我!

       

posted @ 2019-04-08 08:25  大魔王先生  阅读(4527)  评论(3编辑  收藏  举报