RabbitMQ? Kafka? RocketMQ?

消息队列是什么

消息队列是分布式系统中重要的组件,它可以看作是一个存放消息的容器,我们可以产生消息供别人使用,也可以取出别人产生的消息供自己使用

消息队列的作用

老生常谈的问题了,应当张口就来:异步、解耦、削峰

异步

假设我们有一个订单系统,用户支付完成,流程就结束了,但后来搞了个积分系统以后,产品经理要求在用户支付完成后在积分系统内增加积分,过两天又为了用户体验要求在支付完成后给用户发送短信,一周后又提了个用户支付完成后邮件发送电子发票的需求,导致整个流程就变的比较复杂,如图

假设每个环节都耗时 200ms,原来 200ms 就能结束的流程现在变为了 800ms,再有后续的操作的话时间还会更长,但对于用户来说,需要关注的只是支付这一个动作,如果支付完了还得同步等待剩余流程的话,用户体验会非常不好

在用户看来,增加积分、发送短信、发送邮件稍微晚那么几秒也没有关系,而且这三个流程可以同时执行,没有必要的先后顺序,那么我们可考虑将系统做一下改进,如图

改进完成后,用户只用了 200ms 进行支付操作,省下的流程都变为了 异步 操作

解耦

使用线程

乍一看上述的三个流程可以用线程来做,因为线程也可以实现异步,但用线程的话会面临一个问题:强耦合

每次增加一个流程要调用一个接口或者接入 SDK,流程修改完成后还重新发布系统,如果流程多了那么我们不禁要发问了:“我就是个订单系统,发短信和发邮件和我有毛关系啊?!”

不仅如此,如果线程里的三个流程是线性执行的,前面的流程如果报错了会影响后面的逻辑;如果分别用了三个线程去做三个逻辑,第一是浪费资源,其次如果报错了还是免不了负责各个系统的程序员小哥来找你调试,本来能按时下班的,这下又要加班了

使用消息队列

可能有同学会问:“订单系统的流程是走完了,那如果积分系统流程失败了应该怎么办?”,这就涉及到分布式事务了,但对于订单系统来说,将支付成功的消息发送到消息队列就是完成流程了,积分系统的流程成功与失败应该是积分系统处理的事情,这样就做到了 解耦

削峰

每天 0:00 到 12:00,订单系统风平浪静,每秒并发请求数量就 50 个, 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条,但是系统是直接对接 MySQL 的,大量的请求涌入 MySQL,每秒钟执行约 5k 条 SQL

一般的 MySQL,扛到每秒 2k 个请求就差不多了,如果每秒请求到 5k 的话,MySQL 可能直接就挂了,导致系统崩溃,用户也就没法再使用订单系统了。但是高峰期一过,到了下午的时候,就成了低峰期,每秒中的请求数量可能也就 50 个请求,对整个系统几乎没有任何的压力

这个短暂的高峰期积压是没有问题的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是订单系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,订单系统就会快速将积压的消息给解决掉,这样就实现了 削峰

常用消息队列的比较

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级 万级 10 万级,支撑高吞吐 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响     topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性 ms 级 μs级 ms 级 ms 级
可用性 高,基于主从架构实现高可用 同 ActiveMQ 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性 有较低的概率丢失数据 基本不丢 经过参数优化配置,可以做到 0 丢失 同 RocketMQ
功能支持 功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用
社区活跃度

常用消息队列的选择

使用消息队列可能产生的问题

需要根据具体的 MQ 制定相应的解决方案

鸣谢: