05 Mar 2021
When Using MQ
Knowledge about MQ(Message Queue), what senario to use MQ, the difference between MQ products and thinking of scale.
When Using MQ
1.解耦,和业务分离,往mq一写,其他service自己随便取。
# 传统模式
# 系统间耦合性太强,如上图所示,系统 A 在代码中直接调用系统 B 和系统 C 的代码,如果将来 D 系统接入,系统 A 还需要修改代码,过于麻烦!
系统A <-- 接入 -- 系统B
<-- 接入 -- 系统C
? 系统D (系统D也想接入,但是要接入系统A需要改代码)
# MQ模式
# 将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统 A 不需要做任何修改。
系统A --写入--> 消息队列 <--- 系统B
<--- 系统C
<--- 系统D
2.需要异步发送消息。或者网络不通场景。
# 传统模式(120ms)
# 一些非必要的业务逻辑以同步的方式运行,太耗费时间。
用户1 --> 系统B(40ms) --> 系统C(40ms) --> 系统D(40ms)
# MQ模式
# 将消息写入消息队列,非必要的业务逻辑以异步的方式运行(C/D同时读),加快响应速度 45ms
用户1 --> 系统B(40ms) --写入--> 消息队列(耗时5ms) <--订阅-- 系统C
<--订阅-- 系统D
3.消峰,在生产过多可以在queue里面待着慢慢消费,典型就是秒杀业务。
# 传统模式(120ms)
# 并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常
用户1 --写入--> 系统A --写入--> 数据库
用户2 --写入-->
用户3 --写入-->
# MQ模式
# 系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的
用户1 --写入--> 消息队列 <--按照业务规则读取-- 系统A --> 数据库
用户2 --写入-->
用户3 --写入-->
4.最终一致性,mq保证了数据的准确到达。
5.如果有日志采集功能,肯定是首选 kafka了。
引入MQ的缺点:
1.系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低。
2.系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。
MQ Products
- ZeroMQ
- 推特的Distributedlog
- ActiveMQ:Apache旗下的老牌消息引擎
- RabbitMQ、Kafka:AMQP的默认实现。
- RocketMQ
- Artemis:Apache的ActiveMQ下的子项目
- Apollo:同样为Apache的ActiveMQ的子项目的号称下一代消息引擎
- 商业化的消息引擎IronMQ
- 以及实现了JMS(Java Message Service)标准的OpenMQ。
传统的queue就是点对点,经典生产者消费者,你发我收。新的mq比如Kafka主要使用的发布/订阅模式(pub/sub),可以有多个订阅者。
很多传统queue是不持久化数据,发完就删除。
MQ Difference
模式:Kafka是发布/订阅模式(pub/sub),可以有多个订阅者,传统queue大多点对点,经典生产者消费者。
持久化:Kafka是持久化,不会一发送就删除。
高可用:Kafka是分布式,所以不怕宕机。
消息重发:支持at least once,at most once机制。
吞吐量:kafka的TPS大,可以多个Producer和多个Consumer。
顺序消息:Kafka支持(每个分区内部是有序的),传统queue大部分不支持。
消息确认:Kafka支持ack确定(0写了就算成功,1写入master就成功,all全分区写入才成功)模式。
消息回溯:Kafka支持消息回溯offset控制,传统queue大部分不支持。
消息重试:Kafka不支持,但是可以实现(通过分区offset位置回溯),传统queue大部分不支持。
并发:Kafka高并发,可以多个Producer和多个Consumer,这个和吞吐量是一样的。
可以看出Kafka很多场景可以代替传统queue,但是也有缺点:
1.重复消息。Kafka只保证每个消息至少会送达一次(根据设置),虽然几率很小,但一条消息有可能会被送达多次。
2.消息乱序。虽然一个Partition内部的消息是保证有序的,但是如果一个Topic有多个Partition,Partition之间的消息送达不保证有序。
3.复杂性。Kafka需要zookeeper 集群的支持,Topic通常需要人工来创建,部署和维护较一般消息队列成本更高。
4.消费失败不支持重试,不支持事务消息,不支持定时消息,不支持不支持消息查询。
5.Kafka单机超过64个队列/分区,Load会发生明显的飙高现象,队列越多,load越高,发送消息响应时间变长。所以不太适合在线业务使用,主要用户大数据场景。
Conclusion
如果是一个不大的系统,不一定要用消息队列引擎,库就能解决。如果严谨的要求不丢数据,用传统的queue(RabbitMQ)更好一些。
1.Kafka
Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,适合产生大量数据的互联网服务的数据收集业务。
大型公司建议可以选用,如果有日志采集功能,肯定是首选kafka了。
2.RocketMQ
天生为金融互联网领域而生,对于可靠性要求很高的场景,尤其是电商里面的订单扣款,以及业务削峰,在大量交易涌入时,后端可能无法及时处理的情况。
RoketMQ在稳定性上可能更值得信赖,这些业务场景在阿里双11已经经历了多次考验,如果你的业务有上述并发场景,建议可以选择RocketMQ。
3.RabbitMQ
RabbitMQ :结合erlang语言本身的并发优势,性能较好,社区活跃度也比较高,但是不利于做二次开发和维护。不过,RabbitMQ的社区十分活跃,可以解决开发过程中遇到的bug。
如果你的数据量没有那么大,小公司优先选择功能比较完备的RabbitMQ。
分布式的情况,肯定就是kafka了,国外不怎么用RocketMQ吧。
Message Queue Thinking
1.如何保证消息队列高可用?
通过集群机制,master-slave,还有zookeeper管理的集群等方式
2.如何保证消息不被重复消费?
首先queue本身有一些at most once之类的设置,此外在queue基础上进行处理,比如在后面数据库端有key的设置,那么如果有重复数据就不会被插入。
3.如何保证消费的可靠性传输?
不同的MQ处理机制不一样,
1)生产者丢数据:producer需要ACK验证,Kafka则是通过follower和leader的同步避免丢失。
2)消息队列丢数据:通过数据持久化避免丢失,重启后依然存在。
3)消费者丢数据:通过ACK确认已经消费。
4.如果保证消息的顺序性?
在高吞吐量的情况下难以保证,kafka也就是同一个partition内部有序,topic不能保证。
Reference
1.Kafka,Mq,Redis作为消息队列使用时的差异?
2.高并发架构系列:Kafka、RocketMQ、RabbitMQ的优劣势比较
3.一个用消息队列 的人,不知道为啥用 MQ,这就有点尴尬
Til next time,
at 00:00