最近面临面试,作为一个三年经验的工程师,虽然在工作时能力不错,但是由于工作模块的关系,消息队列没有进行接触。因此,在这个方向上失利了很多次。

       首先,为什么我们要用消息队列呢?

       消息队列的作用主要分为三点:1、解耦 2、异步 3、削峰。

       解耦:实际上是用了消息队列的广播功能。例如系统A将生产的数据扔到消息队列中。在这之后,凡是需要这些数据的系统都可以从消息队列中取出A系统发送的消息。生产方和消费方是分离的,从而实现解耦。

       异步:作用在于,两个业务逻辑AB,B依赖于A,A执行快速但B执行的时间比较长。如果AB两个逻辑都处理完才进行返回,那么客户端可能需要等待较长时间。这时可以让A逻辑把消息放到消息队列中,然后快速直接返回,接着B再慢慢的进行消费。当然,异步的这种情况,也能用线程池进行替代。

       削峰:在一个时间,如果大量的请求打到A服务上,由于数据库处理能力有限,在这么大量的请求之下, Sql执行会发生错误,链接会拒绝。这时可以先将请求丢到消息队列中。之后系统再按批拉取数据,进行处理,为高峰期进行了一下缓冲。

       

      消息队列是挺有用的,那么它就是十全十美的吗?它有什么缺点呢?

      当然是有的,按照一般逻辑来说,在原本的系统之上,加了消息队列,那么整个系统就会变得比之前更复杂。这时你不仅要保证原有系统的正确性,还需要保证消息系统的正确性。而且如果消息队列挂了,整个系统也将收到牵连。

      所以在使用消息队列时,有几点是我们需要保证的:1、消息不能被重复消费。2、消息必须可靠。3、消息队列必须是高可用的。

      1、怎么保证消息不被重复消费?

       回答这个问题之前,得解释一下消息怎么会被重复消费。

       RabbitMQ、RocketMQ、Kafka可能都会有重复消费的问题。拿Kafka来说,在客户端批量拉取消息进行消费时,刚消费一半,准备提交offset到zookeeper时,客户端挂了,没提交上。当客户端再次重启时,这些数据会从头进行传递,如果客户端没有去重,就会导致重复消费。

       既然已经消费过消息会再次到达,那么我们就必须保证幂等性。具体保证得看业务的情况。

       例如数据已经存在了,那么就不要插入,update一下。

       例如写的是redis,那就再写一次,因为redis天然幂等。

       例如针对一条消息,在redis中记录一个全局id,然后在消费前,校验一下是否已经存在消费。

       例如在数据库中设置唯一键来保证不会插入多条。

       2、怎么保证消息的可靠性?

       消息队列的不可靠性分三种:消息重复、消息丢失、消息乱序

       消息重复:已经在上面讲了。根本的原因在于已经消费了数据,但是offset没提交。

       消息丢失:生产端原因:生产者发送消息,不等待leader确认,只管发出,而不去确认是否送达。

                         解决:同步模式下,确认机制设置为-1(acks=all/-1),让消息写入Leader和Follower之后再确认消息发送成功。

                                    异步模式下,设置不限制阻塞超时时间,当缓冲区满时不清空缓冲池,而是让生产者处于阻塞状态。

                         消费端原因:消费端先提交了offset,但是在消费时线程被杀掉了,这时会引发消息丢失。

                         解决:在消费完成后,再提交offset进行确认。

        消息乱序:在消息队列进行并行处理时,由于网络故障或速度差异,尽管服务器传递是有序的,但消费者接收的顺序可能不一致。                  

        解决:在生产者写进队列的时候,可以指定一个key,在同一个key下,消息会进入同一个分区(partition),在同一个分区内的消息是有序的。

       3、MQ怎么保证消息必达,即防止消息不丢?

       对于接收消息和消费消息,MQ需要进行确认。拿消息生产者来说,消息生产者在发送消息到MQ-Server上时,MQ-server在接收到消息后,会发送ACK消息给发送方。消息生产者在接到ACK消息后,表示消息已经发送成功。而ACK收到时间超时,则消息生产者会对消息进行重发,如果重试N次还未收到,则回调发送失败。这个过程中,MQ可能会收到一条消息的多次重发。

        4、怎么保证消息队列高可用?

        在kafka中,我们能使用leader-follower(主从备份)保证消息队列的高可用。

        kafka基本架构认知:由多个broker组成,每个broker是一个节点;你创建一个topic,这个topic可以划分成多个partition,每个partition可以存在于不同的broker上,每个partition就放一部分数据。

        所以说,kafka是天然的分布式消息队列。图解如下:

消息队列的作用和问题处理(kafka)

(图片出处:https://blog.csdn.net/u014801403/article/details/80312677

         在kafka0.8之后,提供了replica副本机制,每个partition的数据都会同步到之下的所有replica副本上。而replica会选举出一个leader。之后生产者和消费者都和这个leader打交道,其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上,读的时候就直接读leader上数据即可(这边不能随意读写每个follower,如果能随意读写每个follower,那么就需要care数据一致性的问题,系统复杂度太高,很容易出问题)。

         这么做之后,就有了高可用性,如果某个broker宕机了,那个broker上面的partition在其他机器上都有副本,如果这上面有某个partition的leader,那么此时就会重新选举出一个新的leader来,继续读写那个新的leader即可,这就有了所谓的高可用性。

 

 

           

 

 

                          

      

 

 

        

     

       

              

 

 

相关文章:

  • 2021-11-19
  • 2021-11-19
  • 2021-11-04
  • 2021-11-29
  • 2021-08-17
猜你喜欢
  • 2022-01-17
  • 2021-11-19
  • 2021-06-28
  • 2021-12-17
  • 2021-06-02
  • 2021-11-19
相关资源
相似解决方案