学习笔记
消息队列
1、消息队列的作用及对比
消息队列主要用到的为rabbitMQ,RocketMQ以及kafka,其中rabbitMQ是用erlang编写的比较稳定的MQ,但是不容易深入去掌握;RocketMQ是阿里出品的稳定MQ,但是目前社区不活跃;kafka主要用于大数据领域以及记录日志,可用性非常高。
2、消息队列的作用
- 解耦:作为中间层灵活的配置生产者和消费者
- 异步:将某些耗时请求异步处理
- 削峰:应对突然某个时段的高并发访问
缺点:降低了系统的可用性,提高了系统的复杂性,以及存在信息一致性的问题。
3、如何保证消息队列的可用性
- rabbitMQ:
本质为传统的消息队列。
通过镜像集群模式,每个MQ的节点都对所有的queue数据进行备份同步。
缺点:性能开销大,没法线性扩展。
- Kafka:
天然的分布式消息队列。
0.8版本以后,集群维护一个主从模式,每组partition会选出一个节点作为leader,每次外界的读写操作都只能与leader节点对接,然后再同步到所有的节点。如果leader节点挂了,followers会重新选出一个leader节点。
4、如何保证消息队列不会重复消费
- 问题:
Kafka有个offset作为消息的序号,每次consumer消费了数据之后,每隔一段时间会将offset提交表示已经消费过了。但如果这是系统kill进程重启,会使得consumer来不及提交offset,重启后就会造成重复消费。
- 幂等性:
一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错。
- 如何保证幂等性:
比如数据库加主键或者唯一索引;
或者利用一条全局id,如订单id,每次要消费时去redis里查一下,没有就消费,有则不处理。
5、如何保证消息队列不会丢失消息
- RabbitMQ:
生产者端:
1、开启事务模式,同步操作,提交一个操作会阻塞在那,传输失败则回滚,会造成资源极大的消耗。
2、开启confirm模式,异步操作,每次写消息会分配一个唯一的id,如果传输成功,MQ会回复一个ack消息,如果失败会回复传输失败。
MQ端:
设置持久化,要将queue和消息都持久化。
消费者端:
关闭自动ack机制,在消费者处理完后自行发送ack给MQ。
- Kafka:
消费者端:
关闭自动offset机制,在消费者处理完后自行发送offset给kafka。
Kafka端:
当某个leader挂了时,需要重新选举leader,此时会丢失一些数据未同步。
此时需要通过参数设置,让leader与followers保持联系,确保partitions存在副本等,保证leader切换时不会发生数据丢失。
生产者端:
设置ack=all,即你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了,否则无限重试,则一定不会丢失数据。
6、如何保证消息队列顺序性
- RabbitMQ:
一个queue对应一个consumer,consumer内部用内存队列做排队。
- Kafka:
每个key对应一个内存队列,保证顺序性。
7、消息队列消费端出问题如何解决
- 大量消息积压在mq:
如果消费端如MySQL挂了,会有很多消息积压在mq中,此时应该先修复消费端的问题,然后进行临时扩容去快速消费mq中的消息。
- Mq中的消息过期:
在低峰期手动将消息找出来重新消费。
- Mq积压快满了:
写临时程序来丢弃消息,再在低峰期时手动将消息找回来重新消费。
8、如何自行设计一个消息队列
- 首先需支持可升缩性,需要时可以快速扩容
- 考虑数据持久化方式,顺序落磁盘
- 高可用性,可以参考kafka的leader-follower方案
- 数据0丢失,参考kafka的机制