详细介绍了MQ消息队列的优点和缺点,以及MQ消息队列的选型。
MQ,即Message queue,也就是消息队列,就是一个保存消息的容器。队列是一种先进先出的数据结构,消息队列也是如此,生产者将消息按顺序放入队列,消费者在消费消息时也是按照顺序来取出的。
1 MQ的好处
使用消息队列的核心功能有三个:异步、削峰、解耦。
1.1 异步
通过消息队列异步处理可以提高系统性能,减少响应所需时间。
如果一个接口的业务比较复杂,可以分为很多步,比如在主业务走完之后,还需要发邮件、发短信、记录日志之类的。如果按照传统处理逻辑等所有的业务处理完毕再返回的话,可能会让用户等个1s以上。互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的,因为每次操作等1s是不可接受的。
如果使用MQ,在本系统的主业务处理完毕之后,将其他的非主要业务逻辑发送到消息队列中就直接返回,然后由消息队列的消费者端异步的获取消息并且做相应的业务逻辑,这样就能对用户的请求进行快速的响应,提升用户体验度。
1.2 解耦
通过消息队列可以实现服务之间的解耦,降低系统耦合性,提升系统扩展性。
假设在A服务的某个接口调用成功之后需要通知B、C服务,原始的做法就是B、C服务提供一个接口,然后A服务的最后调用B、C服务的接口(比如Dubbo调用,或者Rest调用)。
上面的做法导致A和B、C服务严重耦合,当A不需要通知B、C服务,或者B、C的接口有变化、或者A需要增加通知D服务时,都需要修改代码。
MQ采用Pub/Sub发布-订阅模式工作,使用MQ之后,A 系统在产生一条数据发送到 MQ 里面去,哪个系统需要数据就自己去 MQ 里面消费。生产者不知道谁会消费以及具体怎么消费,消费者也不知道消息是谁生产的。A系统就不需要维护这么多对其他系统的调用,实现了系统间的解耦,提升了系统的扩展性。
1.3 削锋
如果你的服务器最多的QPS为5000,对于某个接口的调用,可能平时只有1000个并发,那么自然是没问题的,但是在准时秒杀、促销等特殊活动的时候,接口可能达到了10000个并发,那么突然多出了5000个请求,这时你的服务器肯定扛不住而崩溃了。
如果使用MQ,将到来的请求先放入队列里面,然后再消费者端以一定的速度去消费它们,这个速度以你的服务器的QPS决定,当有超过服务器QPS的请求过来时,不能处理的请求都积压在了MQ中,虽然可能会导致请求的响应变慢,但是不至于打垮服务器。因为即使是一些大厂、大电商,它们在做活动的时候,用户也会感觉请求明显变慢,这是允许的。
2 MQ的问题
2.1 系统可用性降低
引入MQ之后,需要考虑到MQ挂掉的情况,如果MQ服务不可用,那么整个系统几乎就不可用了,系统可用性在某种程度上降低。
我们需要保证MQ高可用。
2.2 系统复杂性提高
加入MQ之后,需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题,系统复杂性提高。
上面这些问题我们都要解决。
2.3 数据一致性问题
使用MQ确实具有异步和解耦等好处,但是却带来了数据一致性的问题,生产者发送消息成功后就返回了,那要是消费者消费失败了怎么办呢?如果某个生产者写库失败了,那么数据就不一致了,只有所有的服务都成功才能算这一次请求是成功的。
我们可以使用分布式事务解决数据一致性问题。
3 消息队列的选型
目前在市面上比较主流的消息队列中间件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等这几种。
网上找的对比图如下:
ActiveMQ是一款很早的MQ,之前是很火的,但是由于吞吐量不高,并且数据安全性也不够,现在已经够很少有新项目用了。
RabbitMQ基于AMQP协议来实现,AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅),消息可靠性高,基本不会丢失消息,消息延时很低,达到微秒级,基于Push的模式来处理消息消费,消费状态和订阅关系由服务端负责维护,消息消费完后立即删除,不保留历史消息。但由于RabbitMQ是使用Erlang语言开发的,目前国内很少有公司在做Erlang语言的深入研究,这必然导致二次开发-维护的成本更高,公司很难掌控,加上吞吐量也不高,现在国内已经够很少使用了。
RocketMQ是阿里开源的消息中间件,目前归属于Apache顶级项目。具有高吞吐量、高可用性、适合大规模分布式系统应用的特点,基于Pull的模式来处理消息消费。在国内它被阿里以及其他公司大量使用,并且它是纯Java开发的,对于Java工程师来说比较好深入了解。
Kafka使用Scala语言开发,是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点是基于Pull的模式来处理消息消费,消费状态和订阅关系由客户端端负责维护,消息消费完后不会立即删除,会保留历史消息。核心功能比较少,追求高吞吐量,一开始的目的就是用于日志收集和传输,适合产生大量数据的互联网服务的数据收集业务。目前在世界范围内,Kafka都可以算作消息队列的标杆。
Kafka和RocketMQ都是天然的分布式架构,能够做到高可用,多个副本的数据也能做到0丢失,但是ActiveMQ和RabbitMQ却不是分布式架构。
ActiveMQ和RabbitMQ的GatHub社区活跃度也不如RocketMQ和Kafka。
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!