MQ面试相关知识总结

一、MQ用途

        主要有3点:解耦、异步、削峰(限流)。

        大致图形如下,其实就是在服务与服务之间增加了一个中间件,可以实现上面的三种用途。

解耦:我们看到,服务A强依赖服务B和C,当服务B或者C挂掉后,会直接导致服务A的不可用,这显然不是我们所期望的。比如服务的最后一步是记录日志,但是该服务挂了,虽然日志服务和主流服务没有必然的业务联系,但是因为代码的耦合性过高,直接导致整个服务响应失败。

异步:假如服务A本身执行只需要10ms,服务B需要5ms,服务C(日志服务)需要1s。同样的,一个和业务本身无关的服务过长的响应时间导致了整体服务的响应超时。

削峰:假如由于服务C只是记录日志的,服务器配置较低,1s只能处理2000条数据,但是高峰时段,每秒的请求高达10万笔,过高的请求会导致服务器崩溃。

可以看到,其实上面所讲的三种情况,都很类似,连起来可以这么理解。高峰时段导致服务C运行越来越慢,产生了“异步”所说的问题,如果长时间没解决,可能会导致“解耦”所说的情况,即服务挂掉。

在增加了MQ以后,我们可以在服务A执行完核心业务后,将后续处理的业务数据打入消息队列中,然后就可以返回成功。然后日志服务从消息队列中取到对应的消息进行处理即可。这样就实现了“解耦”和“异步”。在高峰时段,所有的数据都会打入消息队列中,而日志服务只需要按照自己的消费水平(2000条/s)取数据就好,保证了服务的平滑稳定。

二、MQ选型

        主流的MQ有4种,ActiveMQ、RabbitMQ、RocketMQ和Kafka。不过ActiveMQ虽然框架成熟、曾经是MQ中的王牌,但是现在官方的维护频率越来越低,国内各大公司已经很少才用了,再加上吞吐量不高(比kafka低一个数量级),存在消息丢失的情况,所以现在新项目很少会采用了。

        RabbitMQ是用ErLang语言开发的,性能上是最好的,但是由于ErLang语言不是主流语言,二次开发难度较高,很多想要根据实际情况进行二次开发的公司很少采用。不过如果只是简单使用的话,还是不错的,毕竟它的延时是最少的;并且RabbitMQ有一个最大的好处是它具有可视化界面,操作维护很方便。

        RocketMQ是阿里开源的产品,经过了很多高并发项目的考验(如双十一),性能上是有保证的。纯Java编写,维护性高。我理解的它和kafka最大的不同有两点,一是它支持事务;二是集群结构不一致,它没有主从切换,当leader挂掉后,存在一定的感知时间,然后切换到follower上。

        kafka的特点就是高吞吐量,一般配合大数据类的系统来进行实时数据计算、日志采集,在日志收集领域是事实上的标准。

        另外,springCloud全家桶中,有一个springCloudStream消息驱动框架,该框架很好的封装了MQ操作的相关API,减轻了开发者在MQ方面的代码量,不过该框架只封装了RabbitMQ和kafka这两种MQ。

        综上,如果需要对MQ传输提供事务支持或者解决高并发下的业务解耦,建议采用RocketMQ,微服务框架是dubbo的话,应该也建议用RocketMQ(这个没测过,个人猜测,毕竟都是阿里的产品)。如果是要做日志收集等工作,建议采用kafka。中小型公司使用springCloud全家桶开发的项目中,建议采用RabbitMQ(或者kafka)。

        以上是查资料总结的,由于我平时都是使用的kafka,所以后面都以kafka为例了。

三、重复消费

        重复消费在MQ中是一个重点问题,该问题是如何产生的?

        kafka中有一个消息偏移量offset,每当消费者消费完一条消息时,执行commit,会将offset+1。如果一条消息在消费完以后尚未commit offset,突发宕机,会让zookeeper认为该条消息没有被消费。导致消费者重启后重复消费之前的数据。

        如何避免?

        避免重复消费的问题,与同一个服务被多次调用的问题类似,就是如何解决服务的幂等性。大致有如下几个方案:

        1、利用数据库的唯一性约束。

        2、将数据存入redis中,利用redis天然的幂等性,然后再将数据从redis同步到数据库中

        3、生产者发消息时增加一个唯一id(比如UUID),消费者消费成功后将该UUID存入redis中,每次消费前先查看该UUID是否存在。

四、消息丢失

        消息丢失同样也是MQ中是一个重点问题。由于系统中存在生产者、消费者和MQ本身三个组件,所以需要从这三个方面分别讨论。

        MQ本身丢失:由于kafka的集群是leader/follower模式,leader先接受消息后,再同步给follower,如果leader接收到消息后发生宕机,没来得及同步数据给follower,这时依靠选举机制产生了新的leader,但是它已经永远的失去了这条消息。为了避免这种情况发生,就需要修改kafka的配置,利用kafka自身的特性来解决。

        首先给topic设置replication.factor参数:这个值必须大于1,要求每个partition必须有至少2个副本。

        然后在kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保leader挂了还有一个follower。

        然后在producer端设置acks=all:这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了

        最后在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了。

        这样配置以后,就可以保证只有所有的副本数据都同步成功后,才认为消息发送成功,避免了leader挂掉的情况。

        消费者丢失:kafka有一个自动提交机制,每次接受到消息后自动提交offset。如果消息还未处理就挂掉了,但zk却已经接收到消费成功的通知,显然不合理,所以要避免使用kafka的自动提交,改为手动提交。

        生产者丢失:显然,如果配置了acks=all以后,生产者是不会发生消息丢失的。

        另外,查资料发现RabbitMQ和kafka的消息丢失情况不同,这里补充一个RabbitMQ的处理方式。

        MQ本身丢失:由于RabbitMQ没有集群配置,所以只能依靠持久化到本地的方式来进行备份。如果接收到消息还没来得及备份就挂掉了,就会导致消息丢失。不过这个概率很低。如果发生了,可以利用生产者丢失的方式处理,见下。

        消费者丢失:产生原因不说了。可以采用RabbitMQ提供的ack机制,即关闭RabbitMQ自动ack,然后通过api来调用就行,在确认处理完消息后,手动提交ack通知MQ。

        生产者丢失:可能存在的问题就是生产者发送消息后,网络传输有问题导致了数据丢失。为了避免这种情况,一般会开启事务机制,保证数据一致性,但是事务机制由于是同步的,会造成系统性能下降,所以可以借鉴分布式事务的理念,即confirm机制。生产者发送消息后,开启异步接受MQ的反馈,收到后,默认消息发送成功,超时后触发消息重发机制。

五、如何确保消息顺序消费。

        这个很简单,只要保证每个消费者或者每个处理线程都对应一个队列即可。

六、消息积压如何处理。

        毕竟流量高峰的时间存在不长,只要最初规划MQ的空间时考虑到流量高峰的容量,一般是不会出现积压的,除非由于代码bug或者消费者宕机。

        这时为了快速处理积压的消息,我们除了修正bug和重启服务器以外,还需要有提前定好的应急方案,即临时扩容消费者,增加消费者处理速度。并且不能设置消息的TTL,保证消息一直存在。

        实在没办法了的终极解决方案,就是抛弃部分消息,然后过了高峰以后,依靠日志等方式人肉维护。。。

        最后,说一个我之前的公司,为了避免重复消费和消息丢失的解决方案,就是在生产者发送消息前和消费者接收消息后,在本地记录一条数据,然后定时对比两者的差异,来确保这两个问题不会发生。同样该方案也可用于处理积压,完全可以抛弃消息,最后依靠生产者记录的数据进行维护。这种方式比较适合业务分离状态的,如购物场景,只要保证用户下单成功即可,后续的出库,赠加积分,赠送优惠券等功能稍缓缓也不碍事,但是如果是时效性较高的业务,比如商品查询,可能商品描述、商品价格、商品图片都是不同的服务在处理,如果一个服务不能正常返回,那这个业务就无法正常开展。这种情况,就建议采用限流策略了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值