消息队列(Message Queue,简称 MQ) 即可为分布式应用系统提供异步解耦合削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。
一、概述
消息队列主要解决了应用耦合、异步处理、流量削峰等问题。
常见的消息队列有 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ
等,而部分数据库如 Redis、MySQL、PhxSQL
也可以实现消息队列的功能。
二、应用场景
消息队列在实际应用中包含如下几个场景:
- 削峰填谷
- 常应用于如秒杀、抢红包等流量脉冲较高的场景,流量过大导致系统超负荷甚至崩溃,或因限制太过导致大量请求失败而影响用户体验
- 异步处理
- 多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间
- 应用耦合
- 多应用通过消息队列对统一消息进行处理,避免接口调用失败导致整个流程失败
- 如交易、订单系统的变动,会对物流、购物车、积分、流计算分析等业务系统进行操作,消息队列可以实现异步通信和应用解耦,确保主站业务的连续性
- 消息驱动的系统
- 系统分为消息队列、消息生产者、消息消费者;
- 生产者负责产生消息,消费者负责对消息进行处理
三、消息队列的两种模式
消息队列包含两种模式,点对点模式和发布/订阅模式
1. 点对点模式
点对点模式包含三个角色:
- 消息队列
- 消息生产者(发送者)
- 消息消费者(接收者)
生产者生产消息,并发送到 queue 中,消息接收者从 queue 中取出并消费消息。
消息被消费后,queue 中不再有存储,故消费者不能再次消费。
特点:
- 每个消息只有一个接收者(Consumer) - 即消息一旦被消费,就不存在与消息队列中了
- 发送者与接收者没有依赖性,发送者发送消息后,不管有没有接收者在运行,都不影响发送者下次发送消息
- 接收者在成功接收消息后需向队列应答成功,以便消息队列删除当前接收的消息
2. 发布/订阅模式
发布/订阅模式包含三个角色:
- 角色主题(Topic)
- 发布者(Publisher)
- 订阅者(Subscriber)
发布者将消息发送到 Topic,系统将这些消息传递给多个订阅者。
特点:
- 每个消息可以有多个订阅者
- 发布者和订阅者之间有时间上的依赖性,针对某个主题(Topic)的订阅者,它必须创建一个订阅者后,才能消费发布者的消息
- 为了消费消息,订阅者需要提前订阅 Topic,并保持在线运行
四、常用的消息队列
1. RabbitMQ
RabbitMQ 是在 AMQP(高级消息队列协议)基础上完成的,是可复用的企业消息系统。
主要特性:
- 可靠性:提供了多种技术在性能和可靠性间进行权衡,包括持久性机制、投递确认、发布者证实、高可用机制;
- 灵活的路由:消息在到达队列前是通过交换机进行路由的。RabbitMQ 为路由逻辑提供了多种内置交换机类型,你也可以实现自己的交换机类型,并当做 RabbitMQ 的插件使用;
- 消息集群:在相同局域网中,多个 RabbitMQ 服务器可以聚合使用,作为一个独立的逻辑代理;
- 队列高可用:队列可以在集群中的机器上进行镜像,确保消息安全;
- 多种协议的支持:支持多种消息队列协议;
- 服务器端用 Erlang 编写,支持所有编程语言;
- 管理界面:简易的用户界面,方便监控和管理消息;
- 跟踪机制:RabbitMQ 提供消息跟踪机制,可以通过该机制查找异常的过程;
- 插件机制
使用前需要:
- Erlang 语言包
- RabbitMQ 安装包
优点:
- 由 Erlang 编写,MQ 性能较好,高并发
- 健壮、稳定、易用、跨平台、支持多语言、文档齐全
- 有消息确认机制和持久化机制,可靠性高
- 高度可定制的路由
- 管理界面丰富,有许多互联网公司的使用经验
- 社区活跃度高
缺点:
- 由 Erlang 编写,不利于二次开发和维护
- 消息在发送至客户端前,通过代理架构在中央节点上排队。该特性使得 RabbitMQ 易于使用和部署,但影响运行速度,中央节点增加延迟,且对消息的封装比较大
- 需要学习复杂的接口和协议,学习和维护成本较高
2. ActiveMQ
ActiveMQ 由 Apache 出品,完全支持 JMS Provider。快速、支持多语言的客户端和协议,且非常容易嵌入到企业应用环境中,具有许多高级功能。
主要特性:
- 服从 JMS 规范:JMS 规范提供了良好的标准和保证,包括:同步或异步的消息分发,一次和仅一次的消息分发,消息接收和订阅等;
- 连接性:支持的连接协议有:HTTP/HTTPS、IP 多播、SSL、STEMP、TCP、UDP、XMPP 等,众多的协议支持让 ActiveMQ 拥有了很好地灵活性;
- 支持协议种类多:OpenWire、STOMP、REST、XMPP、AMQP;
- 持久化插件和安全插件;
- 支持的客户端语言种类多:Java、C、C++、.NET、Perl、PHP、Python、Ruby;
- 代理集群:多个 ActiveMQ 代理可以组成一个集群来提供服务;
- 异常简单的管理:ActiveMQ 是以开发者思维被设计的
使用 ActiveMQ 需要:
- Java JDK
- ActiveMQ 安装包
优点:
- 跨平台(运行在任何 JVM 上 - Java 的跨平台特性)
- 可以用 JDBC:将数据持久化到数据库,虽然会降低 ActiveMQ 的性能,但数据库是开发人员最熟悉的存储介质
- 支持 JMS 的统一接口
- 支持自动重连
- 有安全机制:支持基于 Shiro、Jaas 等多种安全配置机制,可以对 Queue/Topic 进行认证和授权
- 监控完善:拥有完善的监控,包括 Web console、JMX、Shell、RestAPI;
- 界面友善:提供的 Web Console 能够满足大部分情况,还有第三方组件可以使用
缺点:
- 社区活跃度不如 RabbitMQ 高
- 莫名丢失消息
- 对 5.X 的维护较少,重心在 6.0 以后
- 不适用于上千个队列的应用场景
3. RocketMQ
RocketMQ 由阿里开源,Java 语言实现,在设计时参考了 Kafka 并进行了一些改进,消息可靠性上比 Kafka 更好。在阿里集团内部广泛应用在订单、交易、充值、流计算、消息推动、日志流式处理、binglog 分发等场景。
主要特性:
- 是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点
- Producer、Consumer、队列都可以分布式
- Producer 向队列轮流发送消息
- 队列集合称为 Topic
- Consumer 如果做广播消费,则一个 Consumer 实例消费这个 Topic 的所有队列;如果做集群消息,则多个 Consumer 实例平均消费这个 Topic 的队列集合
- 能够保证严格的消息顺序
- 提供丰富的消息拉取模式
- 高效的订阅者水平扩展能力
- 实时的消息订阅机制
- 亿级消息堆积能力
- 较少的依赖
使用需要:
- Java JDK(RocketMQ 可以运行在 Java 语言支持的平台上)
- Git、Maven
- Rocket 安装包
优点:
- 单机支持 1W 以上持久化队列
- RocketMQ 的所有消息都是持久化的,先写入 PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据;
- 访问时直接从内存读取
- 模型简单,接口易用(JMS 接口很多场合不太实用)
- 性能非常好,可以大量堆积消息在 broker 中
- 各个环节分布式扩展设计,主从 HA
- 开发度较活跃,版本更新很快
缺点:
- 支持的客户端语言不多,主要是 java 和 c++,c++不成熟
- RocketMQ 的社区关注度及成熟度不及前两者
- 没有 web 管理界面,提供了一个 CLI 管理工具来查询、管理和诊断问题
- 没有再 MQ 核心中实现 JMS 接口
4. Kafka
Apache Kafka 是一个分布式消息发布订阅系统。快速、可扩展并且可持久化,它的分区特性,可复制和可容错都是不错的特性。
主要特性:
- 快速持久化,可以在 O(1)的系统开销下持久化消息
- 高吞吐,在一台普通服务器上可以达到 10W/s 吞吐速率
- 完全的分布式系统,Broker、Producer、Consumer 都原生自动支持分布式,自动实现负载均衡
- 支持同步和异步复制两种 HA
- 支持数据批量发送和拉取
- zero-copy:减少 IO 操作步骤
- 数据迁移、扩容对用户透明
- 无需停机即可扩展及其
- 其他特性:严格的消息顺序、丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制
使用 Kafka 需要:
- Java JDK
- Kafka 安装包
优点:
- 客户端语言丰富,支持 java、.net、php、ruby、python、go 等多种语言
- 性能卓越,单机写入 TPS 约在百万条/秒,消息大小 10 字节
- 提供完全分布式架构,并有 replica 机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积
- 支持批量操作
- 消费者采用 pull 方式获取消息,消息有序,通过控制能够保证所有消息被消费且仅被消费一次
- 有优秀的第三方 Web 管理界面:Kafka-Manager
- 在日志领域比较成熟,被多家公司和开源项目使用
缺点:
- Kafka 单机超过 64 个队列/分区,Load 会明显飙高,队列越多,load 越高,发送消息响应时间变长
- 使用短轮询方式,实时性取决于轮询间隔时间
- 消费失败不支持重试
- 支持消息顺序,但一台代理宕机后,就会产生消息乱序
- 社区更新较慢
五、RabbitMQ/ActiveMQ/RocketMQ/Kafka 对比
RabbitMQ | ActiveMQ | RocketMQ | Kafka | |
---|---|---|---|---|
所属社区 | Mozilla | Apache | Ali | Apache |
成熟度 | 成熟 | 成熟 | 比较成熟 | 成熟 |
授权方式 | 开源 | 开源 | 开源 | 开源 |
开发语言 | Erlang | Java | Java | Java&Scala |
客户端支持语言 | 几乎支持所有语言 | Java、C、C++、Python、PHP、Perl、.net 等 | Java、C++(不成熟) | 官方支持 Java;开源社区支持多版本:PHP、Python、GO、C、C++、Ruby、NodeJS 等 |
协议支持 | 多协议支持:AMQP、XMPP、SMTP、STOMP | OpenWire、STOMP、REST、XMPP、AMQP | 自定义协议(社区提供 JMS-不成熟) | 自有协议(社区封装 HTTP 协议支持) |
消息批量操作 | 不支持 | 支持 | 支持 | 支持 |
消息推拉模式 | 多协议,Pull/Push 均有支持 | 多协议,Pull/Push 均有支持 | 多协议,Pull/Push 均有支持 | Pull |
HA | 主从模式,master 提供服务,slave 仅备份 | 基于 Zookeeper+LevelDB 的主从实现方式 | 支持:多 Master 模式、多 Master 多 Slave 模式、一部复制模式,同步双写 | 支持 replica 机制,leader 宕机后,备份自动顶替,并重新选举 leader(基于 Zookeeper) |
数据可靠性 | 可以保证数据不丢,有 slave 备份 | master/slave | 支持异步实时刷盘,同步刷盘,同步复制,异步复制 | 数据可靠,并且有 replica 机制,有容错容灾能力 |
单机吞吐量 | 其次(万级) | 最次(万级) | 最高(十万级) | 次之(十万级) |
消息延迟 | 微妙级 | \ | 比 Kafka 快 | 毫秒级 |
持久化能力 | 内存、文件,支持数据堆积;但数据堆积会影响生产速率 | 内存、文件、数据库 | 磁盘文件 | 磁盘文件,理论上可以无限堆积 |
是否有序 | 单客户端有序 | 可以支持 | 有序 | 多 Client 保证有序 |
事务 | 不支持 | 支持 | 支持 | 不支持,但可以通过 Low Level API 保证仅消费一次 |
集群 | 支持 | 支持 | 支持 | 支持 |
负载均衡 | 支持 | 支持 | 支持 | 支持 |
管理界面 | 较好 | 一般 | 命令行界面 | 官方只提供了命令行版(社区有 Kafka-Manager) |
部署方式 | 独立 | 独立 | 独立 | 独立 |
六、结论
Kafka 在于分布式结构;RabbitMQ 基于 AMQP 协议实现;RocketMQ 思路来源于 Kafka,改成了主从结构,在事务性、可靠性方面做了优化。
广泛来说,电商、金融等对事务性要求很高的,可以考虑 RabbitMQ 和 RocketMQ,对性能要求高的可以考虑 Kafka。