消息队列(Message Queue,简称 MQ)作为构建分布式互联网应用的关键组件,松耦合的架构设计能显著提升系统的可用性与可扩展性。在分布式系统中扮演着至关重要的角色,主要承担着实现异步消息传递、应用解耦、流量削峰以及消息通讯等关键功能。
一、消息队列的应用场景
1、异步处理
以用户注册并需发送注册邮件和短信为例。用户完成注册后,系统需要执行发送注册邮件和注册短信这两项操作。传统的处理方式有串行和并行两种,具体如下:
1)串行方式:先将注册信息成功写入数据库,接着发送注册邮件,最后发送注册短信。只有当这三个任务全部完成后,才会向客户端返回结果。
2)并行方式:在将注册信息成功写入数据库后,同时发送注册邮件和注册短信。同样,要等这三个任务都完成,再向客户端返回结果。与串行方式相比,并行方式能缩短处理时间。假设每个业务节点的处理时间均为 50 毫秒,不考虑网络等其他开销,那么串行方式的总耗时为 150 毫秒,并行方式的总耗时则可能为 100 毫秒。由于 CPU 在单位时间内处理的请求数是固定的,假设 CPU1 秒内的吞吐量是 100 次,那么串行方式 1 秒内 CPU 可处理的请求量约为 7 次(1000ms/150ms),并行方式则约为 10 次(1000ms/100ms)。
而引入消息队列后,对于非必须的业务逻辑,可采用异步处理方式。改造后的架构下,用户的响应时间相当于注册信息写入数据库的时间,即 50 毫秒。因为注册邮件和发送短信的请求写入消息队列后便直接返回,且写入消息队列的速度极快,耗时基本可忽略不计。如此一来,系统的吞吐量可提升至每秒 20 QPS,相比串行方式提高了 3 倍,相比并行方式提高了 2 倍。
2、应用解耦
以用户下单购买业务为例。在传统模式中,用户下单后,订单系统需要调用库存系统的接口来通知库存系统。这种模式存在明显缺点:一是若库存系统无法访问,订单减库存操作就会失败,进而导致订单失败;二是订单系统与库存系统存在紧密耦合。
引入应用消息队列后的方案则有效解决了这些问题:
1)订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,随后向用户返回订单下单成功的信息。
2)库存系统:通过订阅下单的消息,采用拉取或推送的方式获取下单信息,然后根据这些信息进行库存操作。
即便在下单时库存系统无法正常使用,也不会影响用户正常下单,因为订单系统在将信息写入消息队列后,就不再关注后续操作了。这就实现了订单系统与库存系统的应用解耦。
3、流量削峰
流量削峰是消息队列的又一常用场景,在秒杀或团抢活动中应用广泛。秒杀活动往往会因参与人数过多导致流量暴增,极易使应用崩溃。为解决这一问题,可在应用前端加入消息队列,其作用主要有两点:一是能够控制参与活动的人数;二是可以缓解短时间内的高流量对应用造成的压力。
具体实现方式如下:
1)服务器接收用户的请求后,首先将其写入消息队列。若消息队列的长度超过最大限制,就直接抛弃该用户请求或引导用户跳转到错误页面。
2)秒杀业务根据消息队列中的请求信息,进行后续的处理。
4、消息通讯
消息通讯是指消息队列通常内置了高效的通信机制,因此也可用于消息通讯。例如,可实现点对点消息队列或聊天室等功能。这实际上对应了消息队列的两种消息模式,即点对点模式和发布订阅模式。
二、常用消息队列各项对比
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka | |
生产者消费者模式(Producer-Consumer) | 支持 | 支持 | 支持 | 支持 | |
发布订阅模式(Publish-Subscribe) | 支持 | 支持 | 支持 | 支持 | |
请求回应模型(Request-Reply) | 支持 | 支持 | 不支持 | 不支持 | |
API 完备性 | 高 | 高 | 高 | 高 | |
多语言支持 | 支持 | 支持 | 只支持 JAVA | 支持 | |
单机吞吐量 | 万级 | 万级 | 万级 | 十万级 | |
消息延迟 | 无 | 微秒级 | 毫秒级 | 毫秒级 | |
可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 非常高(分布式) | |
消息丢失 | 低 | 低 | 理论上不会丢失 | 理论上不会丢失 | |
文档的完备性 | 高 | 高 | 高 | 高 | |
提供快速入门 | 有 | 有 | 有 | 有 | |
社区活跃度 | 高 | 高 | 中 | 高 | |
商业支持 | 无 | 无 | 阿里云 | 主流云服务厂商 |
三、ActiveMQ、RabbitMQ、RocketMQ、Kafka 特点
ActiveMQ
ActiveMQ 是一个历史悠久的开源项目,在众多产品中都有应用。它实现了 JMS1.1 规范,能与 spring-jms 轻松融合,还支持多种协议。不过,它不够轻巧(源代码比 RocketMQ 多),支持持久化到数据库,但在队列数量较多的情况下,表现欠佳。
RabbitMQ
相比 Kafka,RabbitMQ 更为成熟,支持 AMQP 事务处理。在可靠性方面,它超过 Kafka;在性能方面,又优于 ActiveMQ。
RocketMQ
RocketMQ 是阿里开源的消息中间件,目前正在 Apache 进行孵化。它采用纯 Java 开发,具有高吞吐量、高可用性的特点,适合大规模分布式系统应用。其设计思路源于 Kafka,但并非简单复制,而是对消息的可靠传输及事务性进行了优化。目前,RocketMQ 在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog 分发等场景,支撑了阿里多次双十一活动。由于它是阿里从实践中发展而来的产品,部分接口和 API 的通用性不是很强。但它的可靠性毋庸置疑,且在性能上与 Kafka 一脉相承甚至更优,还支持海量消息堆积。
Kafka
Kafka 的设计初衷是处理日志,不支持 AMQP 事务处理,可将其视为一个日志系统,针对性很强,因此并不具备成熟 MQ 应有的全部特性。不过,Kafka 的性能(吞吐量、tps)强于 RabbitMQ,在处理大数据量的快速处理场景中,比 RabbitMQ 更具优势。