【126期】消息队列面试连环炮
共 8060字,需浏览 17分钟
·
2021-01-29 07:10
阅读本文大概需要 12 分钟。
目录
项目里怎么样使用 MQ 的?
为什么要使用消息队列?
消息队列有什么优点和缺点?
kafka,activemq,rabbitmq,rocketmq 都有什么去呗?
如何保证消息队列高可用?
如何保证消息不被重复消费?
如何保证消息的可靠性传输?
如何保证消息的顺序性?
写一个消息队列架构设计?
消息队列技术选型
解耦
异步
削峰
不用 MQ 系统耦合场景
使用 MQ 系统解耦场景
维护这个代码,不需要考虑人家是否调用成功,失败超时
如果新系统需要数据,直接从 MQ 里消费即可,如果某个系统不需要这条数据就取消对 MQ 消息的消费即可。
不用 MQ 同步高延迟请求场景
使用 MQ 进行异步化之后的接口性能优化
没有用 MQ 时高峰期系统被打死的场景
使用 MQ 进行削峰的场景
算一笔账,每秒积压在 MQ 里消息有 3000 条,一分钟就会积压 18W 条消息,一个小时就会积压 1000 万条消息。等高峰期一过,差不多需要 1 个多小时就可以把 1000W 条积压的消息给处理掉
系统可用性降低
系统复杂性变高
一致性问题
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点
消息队列高可用
RabbtitMQ 高可用
单机模式
普通集群模式(非高可用)
优点:可以多个机器消费消息,可以提高消费的吞吐量
缺点:可能会在 rabbitmq 内部产生大量的数据传输 ;可用性基本没保障,queue 所在机器宕机,就没办法消费了
镜像集群模式(高可用,非分布式)
Kafka 高可用架构
Kafka 0.8版本之前是没有 HA 机制的,任何一个 broker 宕机了,那么就缺失一部分数据。
Kafka 0.8以后,提供了 HA 机制,就是 replica 副本机制。
leader和follower的同步机制:
消息队列重复数据
Kafka 消费端可能出现的重复消费问题
重复消息原因:(主要发生在消费者重启后)
保证 MQ 重复消费幂等性
思路:
拿数据要写库,首先检查下主键,如果有数据,则不插入,进行一次update
如果是写 redis,就没问题,反正每次都是 set ,天然幂等性
生产者发送消息的时候带上一个全局唯一的id,消费者拿到消息后,先根据这个id去 redis里查一下,之前有没消费过,没有消费过就处理,并且写入这个 id 到 redis,如果消费过了,则不处理。
基于数据库的唯一键
保证 MQ 消息不丢
RabbitMQ可能存在的数据丢失问题
生产者写消息的过程中,消息都没有到 rabbitmq,在网络传输过程中就丢了。或者消息到了 rabbitmq,但是人家内部出错了没保存下来。
RabbitMQ 接收到消息之后先暂存在主机的内存里,结果消费者还没来得及消费,RabbitMQ自己挂掉了,就导致暂存在内存里的数据给搞丢了。
消费者消费到了这个消费,但是还没来得及处理,自己就挂掉了,RabbitMQ 以为这个消费者已经处理完了。
channel.txSelect
try {
//发送消息
} catch(Exception e){
channel.txRollback;
//再次重试发送这条消息
}
channel.txCommit;
先把 channel 设置成 confirm 模式
发送一个消息到 rabbitmq
发送完消息后就不用管了
rabbitmq 如果接收到了这条消息,就会回调你生产者本地的一个接口,通知你说这条消息我已经收到了
rabbitmq 如果在接收消息的时候报错了,就会回调你的接口,告诉你这个消息接收失败了,你可以再次重发。
public void ack(String messageId){
}
public void nack(String messageId){
//再次重发一次这个消息
}
创建queue的时候将其设置为持久化的,这样就可以保证 rabbitmq持久化queue的元数据,但是不会持久化queue里的数据
发送消息的时候将 deliveryMode 设置为 2,将消息设置为持久化的,此时 rabbitmq就会将消息持久化到磁盘上去。必须同时设置 2 个持久化才行。
持久化可以跟生产者那边的 confirm机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 ack了 ,所以哪怕是在持久化到磁盘之前 ,rabbitmq挂了,数据丢了,生产者收不到 ack,你也可以自己重发。
Kafka 可能存在的数据丢失问题
消费端弄丢数据
Kafka 丢掉消息
解决方案:(保证 kafka broker端在 leader发生故障,或者leader切换时,数据不会丢)
给 topic设置 replication.factor ,这个值必须大于 1,保证每个 partition 必须至少有 2 个副本
在 kafka 服务端设置 min.insync.replicas 参数,这个值必须大于 1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保 leader挂了还有一个follower,保证至少一个 follower能和leader保持正常的数据同步。
在 producer 端设置 acks =all,这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。否则会生产者会一直重试,此时设置 retries = MAX(很大的重试的值),要求一旦写入失败,就卡在这里(避免消息丢失)
kafka 生产者丢消息
消息队列顺序性
rabbitmq,一个queue,多个consumer,这不明显乱了
kafka,一个topic,一个partition,一个consumer,内部多线程,这不也乱了
RabbitMQ 消息顺序错乱
RabbitMQ 如何保证消息顺序性
Kafka 消息顺序错乱
Kafka 保证消息顺序性
消息队列延迟以及过期失效
每次消费之后都要写 mysql,结果mysql挂了,消费端 hang 不动了。
消费者本地依赖的一个东西挂了,导致消费者挂了。
长时间没处理消费,导致 mq 写满了。
快速处理积压的消息
先修复 consumer 的问题,确保其恢复消费速度,然后将现有的 consumer 都停掉
新建一个topic,partition是原来的 10 倍,临时建立好原先 10 倍或者 20 倍的 queue 数量
然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue
接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据
这种做法相当 于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常 10 倍速度
等快速消费完积压数据之后,恢复原先部署架构 ,重新用原先的 consumer机器消费消息
这个时候开始写程序,将丢失的那批 数据查出来,然后重新灌入mq里面,把白天丢的数据补回来。
如何设计消息队列中间件架构
mq要支持可伸缩性,快速扩容。设计一个分布式的 MQ,broker->topic->partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够,给 topic 增加 partition ,然后做数据迁移,增加机器。
mq数据落磁盘,避免进程挂了数据丢了,顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这个就是 kafka的思路。
mq高可用性。多副本->leader & follower-> broker 挂了重新选举 leader 对外提供服务
支持数据 0 丢失。
推荐阅读:
【125期】举例说明消息队列应用场景及ActiveMQ、RocketMQ、Kafka等的对比
微信扫描二维码,关注我的公众号
朕已阅