《消息队列》读书笔记

消息队列(Message Queue,简称 MQ) 即可为分布式应用系统提供异步解耦合削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。

一、概述

消息队列主要解决了应用耦合、异步处理、流量削峰等问题。

常见的消息队列有 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ等,而部分数据库如 Redis、MySQL、PhxSQL 也可以实现消息队列的功能。

二、应用场景

消息队列在实际应用中包含如下几个场景:

  1. 削峰填谷
    1. 常应用于如秒杀、抢红包等流量脉冲较高的场景,流量过大导致系统超负荷甚至崩溃,或因限制太过导致大量请求失败而影响用户体验
  2. 异步处理
    1. 多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间
  3. 应用耦合
    1. 多应用通过消息队列对统一消息进行处理,避免接口调用失败导致整个流程失败
    2. 如交易、订单系统的变动,会对物流、购物车、积分、流计算分析等业务系统进行操作,消息队列可以实现异步通信和应用解耦,确保主站业务的连续性
  4. 消息驱动的系统
    1. 系统分为消息队列、消息生产者、消息消费者;
    2. 生产者负责产生消息,消费者负责对消息进行处理

三、消息队列的两种模式

消息队列包含两种模式,点对点模式和发布/订阅模式

1. 点对点模式

点对点模式包含三个角色:

  • 消息队列
  • 消息生产者(发送者)
  • 消息消费者(接收者)

生产者生产消息,并发送到 queue 中,消息接收者从 queue 中取出并消费消息。

消息被消费后,queue 中不再有存储,故消费者不能再次消费。

特点:

  • 每个消息只有一个接收者(Consumer) - 即消息一旦被消费,就不存在与消息队列中了
  • 发送者与接收者没有依赖性,发送者发送消息后,不管有没有接收者在运行,都不影响发送者下次发送消息
  • 接收者在成功接收消息后需向队列应答成功,以便消息队列删除当前接收的消息

2. 发布/订阅模式

发布/订阅模式包含三个角色:

  • 角色主题(Topic)
  • 发布者(Publisher)
  • 订阅者(Subscriber)

发布者将消息发送到 Topic,系统将这些消息传递给多个订阅者。

特点:

  • 每个消息可以有多个订阅者
  • 发布者和订阅者之间有时间上的依赖性,针对某个主题(Topic)的订阅者,它必须创建一个订阅者后,才能消费发布者的消息
  • 为了消费消息,订阅者需要提前订阅 Topic,并保持在线运行

四、常用的消息队列

1. RabbitMQ

RabbitMQ 是在 AMQP(高级消息队列协议)基础上完成的,是可复用的企业消息系统。

主要特性:

  1. 可靠性:提供了多种技术在性能和可靠性间进行权衡,包括持久性机制、投递确认、发布者证实、高可用机制;
  2. 灵活的路由:消息在到达队列前是通过交换机进行路由的。RabbitMQ 为路由逻辑提供了多种内置交换机类型,你也可以实现自己的交换机类型,并当做 RabbitMQ 的插件使用;
  3. 消息集群:在相同局域网中,多个 RabbitMQ 服务器可以聚合使用,作为一个独立的逻辑代理;
  4. 队列高可用:队列可以在集群中的机器上进行镜像,确保消息安全;
  5. 多种协议的支持:支持多种消息队列协议;
  6. 服务器端用 Erlang 编写,支持所有编程语言;
  7. 管理界面:简易的用户界面,方便监控和管理消息;
  8. 跟踪机制:RabbitMQ 提供消息跟踪机制,可以通过该机制查找异常的过程;
  9. 插件机制

使用前需要:

优点:

  1. 由 Erlang 编写,MQ 性能较好,高并发
  2. 健壮、稳定、易用、跨平台、支持多语言、文档齐全
  3. 有消息确认机制和持久化机制,可靠性高
  4. 高度可定制的路由
  5. 管理界面丰富,有许多互联网公司的使用经验
  6. 社区活跃度高

缺点:

  1. 由 Erlang 编写,不利于二次开发和维护
  2. 消息在发送至客户端前,通过代理架构在中央节点上排队。该特性使得 RabbitMQ 易于使用和部署,但影响运行速度,中央节点增加延迟,且对消息的封装比较大
  3. 需要学习复杂的接口和协议,学习和维护成本较高

2. ActiveMQ

ActiveMQ 由 Apache 出品,完全支持 JMS Provider。快速、支持多语言的客户端和协议,且非常容易嵌入到企业应用环境中,具有许多高级功能。

主要特性:

  1. 服从 JMS 规范:JMS 规范提供了良好的标准和保证,包括:同步或异步的消息分发,一次和仅一次的消息分发,消息接收和订阅等;
  2. 连接性:支持的连接协议有:HTTP/HTTPS、IP 多播、SSL、STEMP、TCP、UDP、XMPP 等,众多的协议支持让 ActiveMQ 拥有了很好地灵活性;
  3. 支持协议种类多:OpenWire、STOMP、REST、XMPP、AMQP;
  4. 持久化插件和安全插件;
  5. 支持的客户端语言种类多:Java、C、C++、.NET、Perl、PHP、Python、Ruby;
  6. 代理集群:多个 ActiveMQ 代理可以组成一个集群来提供服务;
  7. 异常简单的管理:ActiveMQ 是以开发者思维被设计的

使用 ActiveMQ 需要:

  • Java JDK
  • ActiveMQ 安装包

优点:

  1. 跨平台(运行在任何 JVM 上 - Java 的跨平台特性)
  2. 可以用 JDBC:将数据持久化到数据库,虽然会降低 ActiveMQ 的性能,但数据库是开发人员最熟悉的存储介质
  3. 支持 JMS 的统一接口
  4. 支持自动重连
  5. 有安全机制:支持基于 Shiro、Jaas 等多种安全配置机制,可以对 Queue/Topic 进行认证和授权
  6. 监控完善:拥有完善的监控,包括 Web console、JMX、Shell、RestAPI;
  7. 界面友善:提供的 Web Console 能够满足大部分情况,还有第三方组件可以使用

缺点:

  1. 社区活跃度不如 RabbitMQ 高
  2. 莫名丢失消息
  3. 对 5.X 的维护较少,重心在 6.0 以后
  4. 不适用于上千个队列的应用场景

3. RocketMQ

RocketMQ 由阿里开源,Java 语言实现,在设计时参考了 Kafka 并进行了一些改进,消息可靠性上比 Kafka 更好。在阿里集团内部广泛应用在订单、交易、充值、流计算、消息推动、日志流式处理、binglog 分发等场景。

主要特性:

  1. 是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点
  2. Producer、Consumer、队列都可以分布式
    1. Producer 向队列轮流发送消息
    2. 队列集合称为 Topic
    3. Consumer 如果做广播消费,则一个 Consumer 实例消费这个 Topic 的所有队列;如果做集群消息,则多个 Consumer 实例平均消费这个 Topic 的队列集合
  3. 能够保证严格的消息顺序
  4. 提供丰富的消息拉取模式
  5. 高效的订阅者水平扩展能力
  6. 实时的消息订阅机制
  7. 亿级消息堆积能力
  8. 较少的依赖

使用需要:

  • Java JDK(RocketMQ 可以运行在 Java 语言支持的平台上)
  • Git、Maven
  • Rocket 安装包

优点:

  1. 单机支持 1W 以上持久化队列
  2. RocketMQ 的所有消息都是持久化的,先写入 PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据;
  3. 访问时直接从内存读取
  4. 模型简单,接口易用(JMS 接口很多场合不太实用)
  5. 性能非常好,可以大量堆积消息在 broker 中
  6. 各个环节分布式扩展设计,主从 HA
  7. 开发度较活跃,版本更新很快

缺点:

  1. 支持的客户端语言不多,主要是 java 和 c++,c++不成熟
  2. RocketMQ 的社区关注度及成熟度不及前两者
  3. 没有 web 管理界面,提供了一个 CLI 管理工具来查询、管理和诊断问题
  4. 没有再 MQ 核心中实现 JMS 接口

4. Kafka

Apache Kafka 是一个分布式消息发布订阅系统。快速、可扩展并且可持久化,它的分区特性,可复制和可容错都是不错的特性。

主要特性:

  1. 快速持久化,可以在 O(1)的系统开销下持久化消息
  2. 高吞吐,在一台普通服务器上可以达到 10W/s 吞吐速率
  3. 完全的分布式系统,Broker、Producer、Consumer 都原生自动支持分布式,自动实现负载均衡
  4. 支持同步和异步复制两种 HA
  5. 支持数据批量发送和拉取
  6. zero-copy:减少 IO 操作步骤
  7. 数据迁移、扩容对用户透明
  8. 无需停机即可扩展及其
  9. 其他特性:严格的消息顺序、丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制

使用 Kafka 需要:

  • Java JDK
  • Kafka 安装包

优点:

  1. 客户端语言丰富,支持 java、.net、php、ruby、python、go 等多种语言
  2. 性能卓越,单机写入 TPS 约在百万条/秒,消息大小 10 字节
  3. 提供完全分布式架构,并有 replica 机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积
  4. 支持批量操作
  5. 消费者采用 pull 方式获取消息,消息有序,通过控制能够保证所有消息被消费且仅被消费一次
  6. 有优秀的第三方 Web 管理界面:Kafka-Manager
  7. 在日志领域比较成熟,被多家公司和开源项目使用

缺点:

  1. Kafka 单机超过 64 个队列/分区,Load 会明显飙高,队列越多,load 越高,发送消息响应时间变长
  2. 使用短轮询方式,实时性取决于轮询间隔时间
  3. 消费失败不支持重试
  4. 支持消息顺序,但一台代理宕机后,就会产生消息乱序
  5. 社区更新较慢

五、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。

参考资料

文章作者: koral
文章链接: http://luokaiii.github.io/2019/12/10/后端/消息队列/1.常见的消息队列/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自