消息队列深度面试题
消息队列
用消息队列的好处
-
解耦
- A为核心系统,A发消息到MQ,其他系统到MQ中消费信息即可,不需要A系统修改代码让其他系统调用或者调用其他系统
- 异步
-
削峰
- 在高并发下,将用户的请求数据发送给消息队列后立刻返回,后续再异步将数据写入数据库中
各种MQ的使用场景和优缺点
acticeMQ使用少,生态不好;RabbitMQ,中小型可以用,单机并发1w,缺点是用erlang写的,源码不好读;
rocketMQ,生态好,单机并发1w;kafka,分布式系统用,单机并发10w
MQ的可用性
- MQ如果不做集群,一个MQ挂了,系统就会崩溃了
如何保证消息不被重复消费
-
如果消息是做数据库的插入操作,给每条数据分配一个主键,那么出现重复消费的情况会导致主键冲突,避免数据库出现脏数据;使用唯一键,插入时不允许重复就不会出现脏数据
-
如果是redis的set操作,那么不作处理,因为无论set多少次结果都是一样
-
前面两种都不可行,准备一个第三方服务来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该信息,就以<id, message>的形式写入redis。那消费者开始消费前,先去redis查询此条信息是否被消费过即可
如何保证消息的可靠性传输(数据丢失问题)
-
RabbitMQ可能存在的数据丢失问题
-
在写信息的过程中,因为网络问题导致在传输到rabbitmq的过程中就已经丢失(生产者)
-
事务机制,同步的,生产者发送消息会同步阻塞卡住等待是否成功,会导致生产者发送消息时吞吐量降低
-
把channel设置成confirm模式,如果接收到消息后回调生产者本地的一个接口,通知这条消息已经收到了;如果接收的过程中出错了,回调接口通知消息接收失败,需要再次发送
-
-
raiibitmq接收到消息后先暂存在自己的内存当中,消费者没来得及消费,rabbit就挂掉了(rabbitmq)
- 开启rabbitmq的持久化,持久化磁盘中
-
消费者消费到消息后,自己没来得及处理就挂掉, rabbitmq会认为这个消费者已经处理完了(消费者)
- 关闭autoAck机制(自动通知rabbitmq已经消费到了消息了),手动告诉mq消费了消息
-
-
kafka可能存在的数据丢失问题
-
消费者接收到消息后,自动提交offset告诉kafka已经消费了此条消息了,但是此时消费者挂掉了(消费者)
- 关闭自动提交offset即可,手动进行提交,自己保证幂等性
-
kafa的leader数据在同步到follower的过程中丢失(kafka)
- 在kafka服务端设置至少1个follower;min.insync.replicas必须大于1
- 在producer生产者设置 acks=all,设置后必须是写入所有replica之后,才能认为写成功
- 在生产者设置 retries=MAX,设置无限重试写入
-
总结:
-
保证防止生产者丢失数据、消息队列弄丢数据、消费者弄丢数据
-
生产者丢失数据MQ的事务会回滚,尝试重新发送即可; 消息队列这块不需要担心,因为会持久化到硬盘; 消费者弄丢数据一般都是采用了自动确认信息模式导致消费信息被删,将自动改为手动即可,也就是说消费者消费完之后,调用一个MQ的确认方法就行了
如何保证从消息队列里拿到的数据按顺序执行
(https://www.jianshu.com/p/02fdcb9e8784)
-
rabbitmq: 拆分多个queue,每个queue对应一个consumer,就是多一些queue;一个queue对应一个consumer,consumer内部用内存队列进行排队,然后分发给底层的不同worker处理
-
kafka: 一个topic,一个partiton,一个consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue
如何解决消息队列的延时以及过期失效问题?有几百万信息持续挤压几个小时,怎么解决?
-
消息队列的延迟和过期失效是消息队列的自我保护机制,目的是为了防止本身被挤爆,当然可以关闭保护
-
紧急扩容,修复 消费者的问题,将原有的 consumer 都停掉,新建一个topic,partition是原来的10倍,临时建好原先10倍或者更大的queue数量;然后写一个临时的consumer去消费积压的数据,消费后不做耗时处理,直接写去10倍的queue当中;接着用10倍的机器来部署consumer,每一批consumer消费一个临时的queue(kafka)
-
把大量积压的数据丢弃,。被地丢的信息可以针对该业务,查询出来丢失的数据,重新灌进mq,把丢的数据补回去(rabbitmq)
开发一个消息中间件如何设计架构
-
支持可伸缩性,可快速扩容。broker -> topic -> partition,每个 partition放一个机器,存一部分数据,资源不够了给topic增加partiton,做数据迁移
-
数据落地磁盘,保证数据丢失了能找回来
-
高可用保障机制,leader & follower -> broker挂了重新选举leader对外服务
-
解决数据0丢失的问题
RabbitMQ
-
普通集群模式
- 多台机器,多个节点(queue),数据只在一个queue中存在,消费者消费mq时可以指定随便一个queue,如果这个queue没有数据,会去存在数据的queue中拿,拿到数据后再返回给消费者
-
镜像集群模式
- 多台机器,多个节点,数据会存在每一个节点当中,消费者随便指定一个queue都能获取到数据