MQ在开发中需要注意的问题及寻常解决方案

        在微服务项目对接口响应速度有要求,就会用到MQ做无关重要业务逻辑的异步处理。本章以RabbitMQ举例,列举出使用目的、使用原则、可靠性保证(本章重点)、顺序消费场景。

使用目的

        解耦、异步、削峰。这些名词早已经被玩烂,我们使用MQ最重要的无非就是异步处理,提高响应速度,削峰控制推送消息的速度,不要使消费者服务器过于繁忙导致宕机。

        虽然网上一堆的例子,但为了文章流畅性,找了张图 :

        使用MQ之前

        

        使用MQ之后 

        

         可以看到直接发送一个消息,消息里面带上所需信息,发送过后直接返回结果,减少了等待RPC调用服务执行过程。

        如果面试问到MQ的使用场景,我们应该重点放在异步、其次削峰,解耦的话可能我目前参与的项目不是很好,感知不明显,因为微服务的远程调用其实已经把业务逻辑职责单一了,但MQ作为一个中介等于把调用方和被调用方在代码层面变得没有关系了,这估计就是解耦了吧,但逻辑上还是关联的。

使用原则

        使用一个新技术前要思考他解决什么问题,我上个标题已经解答了,项目用户体系大,某些接口需要请求外部公网可以预料到执行时间比较长时,就可以引入MQ服务,来优化给用户响应速度。但技术选型显然不是本章讨论的内容(因为我还没到做架构的层次),下面列举使用MQ做异步的场景。

        1. 上游(消息发送方)不关心执行结果(就像大部分线程不需要返回值),属于告诉你了,剩下的不关我事了,我不会跟据你的返回结果做逻辑处理。

        2.  消息发送方关心执行结果,但消息接收方执行时间很长(线程需要返回值)。需要他的返回值做逻辑处理,但可以预料到执行时间长。执行流程 消息发送方 -> 消息接收方 (处理完成之后发送) -> 原来的消息发送方。

不适合的场景:

         1. 消息发送方关心执行结果,但服务之间强依赖,如支付等涉及到钱的场景。

可靠性保证

        MQ服务器宕机、消费者网络波动宕机没有处理完等造成的消息丢失,或者重复消费。我以面试题的形式说明解决方案。

        消息丢失问题

         生产者: 

                - 事务(同步不推荐),使用该消息中间件提供的API来开启 提交 回滚
                - ack消息确认(异步推荐)

        MQ服务器:

                - 开启 交换机、队列、持久化

        消费者:

               - 消息确认,手动ack(自动ack,服务器在发送给消费者消息后为成功,直接删除消息,

       设置手动ack可以在逻辑执行完后再删除,不成功重试)

        以上方式针对于单机,但还是有丢失的可能性,如果想要进一步降低丢失概率和提升性能,还可以为mq搭建镜像集群(不同节点有某个节点的备份数据)。如果想要消息完全不丢失是不可能的,所以我把使用原则写在了前面。

       消息重复消费

          场景一:注册服务消费者设置了手动ack,在执行完业务逻辑后确认,在注册成功后插入数据完成,向MQ发送处理邮件消息,最后一步没有执行完由于网络波动宕机(没来得及确认),服务器认为这个消息没有被消费,在消费者重启后再发送,导致插入了两条相同的用户信息,导致了数据的幂等性问题。

        场景二: 生产者可能会重复推送一条数据到 MQ 中,为什么会出现这种情况呢?也许是一个 Controller 接口被重复调用了 2 次,没有做接口幂等性导致的;也可能是推送消息到 MQ 时响应比较慢,生产者的重试机制导致再次推送了一次消息。

        所以消息重复消费问题在于数据的幂等性(数据库中存入了除ID外完全相同的数据)。

        为了保证消息不被重复消费,首先要保证每个消息是唯一的,所以可以给每一个消息携带一      个全局唯一的id,流程如下:

        1、消费者监听到消息后获取id,先去查询这个id是否存中

        2、如果不存在,则正常消费消息,并把消息的id存入 数据库或者redis中(下面的编码示例使用redis)

        3、如果存在则丢弃此消息

        实现方法

        1. 将id存入redis的string中(单消费者场景):这样一个队列,redis数据只有一条,每次消息过来都覆盖之前的消息,但是消费者多的情况不适用,可能会存在问题--一个消息被多个消费者消费。

        2. 将id存入list中(多消费者场景):这个方案可以解决多消费者的问题,但是随着mq的消息增加,redis数据越来越多,需要去清除redis数据。

        3. 将id以key值增量存入string中并设置过期时间:以消息id为key,消息内容为value存入string中,设置过期时间(可承受的redis服务器异常时间,比如设置过期时间为10分钟,如果redis服务器断了20分钟,那么未消费的数据都会丢了)。

顺序消费

大家可以参考这篇文章 ,这位大牛写的非常好(18条消息) MQ中怎样去实现消息的顺序消费_WannaRunning的博客-CSDN博客_mq顺序消费

 

        

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值