如何为微服务选择正确的消息队列
微服务及消息队列简史
自从 Peter Rodgers 博士 2005 年在 Web Services Edge 会议上首次提出 Micro-Web-Services 一词后,IT 行业慢慢地从单体架构转向了微服务。
2009 年,Netflix 决定把其单体架构拆分为微服务。
2010 年,Best Buy 开始把它们的单体架构转变为微服务架构。
2011 年,eBay 开始推行微服务。
2001 年,当时 Amazon 的零售网站还是个巨大的单体架构。
Rob Brigham 在 Amazon 的 re:Invent 2015 会议上提出了微服务改造建议:
-
将服务端拆分成松耦合的微服务
-
隔离微服务时应该关注业务而非团队技术栈
-
成立更小单位的研发团队,专注于定义良好的服务,使他们可以更高效、更大规模的完成交付
今天,服务端基本都遵循微服务架构设计模式。然而,基于消息队列的进程间或线程间通信可以追溯到 20 世纪 80 年代初,当时使用的是 UNIX 的 V API 或其它实时操作系统内核。直到 2000 年代,基于网络间通讯的消息队列才诞生。
-
RabbitMQ 在 2007.07.01 发布了它的 v1.0.0 版本
-
Kafka 最初由 LinkedIn 开发,随后于 2011 年初开源
-
Amazon SQS 于 2004 年底进入测试阶段,并于 2006 年年中正式上市
使用同步通讯还是异步通讯
选择了微服务架构,就面临着选择服务间的通讯协议:
-
同步:HTTP,如 REST、SOAP、RPC、gRPC 等
-
异步:消息队列,如 AMQP、Kafka、MQTT 等
同步通讯更容易出错,更难调试,也更难恢复。如果不是实时性要求特别强的功能,可以考虑异步通讯。
异步通讯提供了单一、可靠的消息总线,使得调试更容易,更不容易出错,服务间数据传递更可靠也更安全。
也就是说,除非存在使用较旧编程语言的遗留服务,或者无法改造的旧基础架构,云原生时代更建议选择异步通讯。
选择了使用异步方法,就需要决定使用什么消息队列中间件和消息队列协议。
消息队列中间件
以下是截至 2021 年比较著名的消息队列中间件以及云服务列表:
-
RabbitMQ
-
Apache Qpid
-
Apache Kafka
-
Apache ActiveMQ
-
Redis
-
Eclipse OpenMQ
-
JoramMQ
-
Eclipse Mosquitto
-
HiveMQ
-
Solace PubSub+
-
Google Cloud Pub/Sub
-
Amazon SQS
-
Amazon MQ
-
IBM MQ
-
Azure Event Hubs
-
Azure Service Bus
消息队列协议矩阵
以下是消息队列协议比较矩阵:
注意:Headers 表示具有任意数量键的 dictionary 或 map,而 attrs 表示一组有限的键值对。
相似点:
-
所有协议都有队列的 FIFO 概念
-
所有协议都基于 TCP
-
所有协议都有生产者、消费者的概念
-
所有协议都有负载(payload)与正文(body)
不同点:
-
AMQP 有不同的消息传递模型
-
Cloud-based 的消息队列具有死信队列
-
消息检索(routing key)方法不同
-
Headers 和 attrs 的支持有限
-
Redis、STOMP、MQTT 的功能最不丰富,而 AMQP 的功能最丰富
-
Cloud-based 的消息队列具有一些独特的配置以及相关 API。
消息队列协议详解
消息队列协议的选择实际上比消息队列中间件的选择更重要。因为如果选择一个更通用的协议,就更容易找到其他中间件作为替代。
AMQP
AMQP - Advanced Message Queue Protocol 是一种基于 TCP 的二进制协议,已成为 ISO 和 OASIS 的标准,AMQP 协议主要由 RabbitMQ 使用。
优点:
-
针对不同的用户场景使用不同的消息传递模型,在协议级别降低了架构的复杂性
-
AMQP 是 ISO 和 OASIS 标准,被广泛采用
-
AMQP 快速、安全,可能是消息队列协议中最成熟的
-
可以在公有云上找到对应的云产品,并且可以在云产品和自有 RabbitMQ 间轻松切换
-
使用 classical 队列或 quorum 队列进行队列镜像,使其易于扩展
-
RabbitMQ 的消息大小限制在版本 3.7.0 之前为 2GB,在版本 3.8.0 中减少到 128MB
缺点:
-
不向下兼容,客户端只实现协议的一个版本,版本之间升级迁移可能很耗时
-
依赖 RabbitMQ 许多插件可能会面临运维挑战
-
调试和监控可能存在问题
-
虽然 AMQP 感觉像是标准化的消息队列协议,但大多数消息队列中间件都不支持它
Apache Kafka
Apche Kafka 既是消息队列中间件的名称,也是协议本身。截至 2021,该协议共有 12 个版本,客户端可同时兼容所有版本。
Apche Kafka 是一个由 Apache 软件基金会开发和维护的项目,用 Scala 和 Java 编写。
Apache Kafka 团队选择定义自己的协议,而不是采用 AMQP 或 STOMP。是因为他们认为协议决定了实现,因此,采用已有的协议会降低了他们创建分布式消息中间件以及进行某些优化的自由度。
优点:
-
Apache Kafka 的 partition 和 replication 的功能使其易于扩展
-
Apache Kafka 提供了一种批量发送小消息的方法,使得该协议非常高效
-
Apache Kafka 提供的管理 API 使调试变得很容易
缺点:
-
Apache Kafka 需要部署 ZooKeeper 和 Kafka 两部分,对于初学者而言具有一定的挑战性
-
Apache Kafka 协议规范不断升级变化,客户端可能很难跟上其变化,升级也可能具有挑战性
-
消息大小限制为 1MB
STOMP
STOMP - Streaming Text Oriented Messaging Protocol 是一种基于文本的协议,与 AMQP 非常相似。但它缺乏其他协议所具有的许多功能和优化。另一方面,STOMP 的简单性使其更易于采用。因此,有许多客户端支持该协议,RabbitMQ 也可以通过插件支持 STOMP。对于一些简单的用例或快速原型,可以考虑使用 STOMP。
优点:
-
简单,易于集成
-
通过插件方式支持 RabbitMQ
缺点:
-
与其他协议相比,功能和优化更少
MQTT
MQTT - Message Queuing Telemetry Transport 是物联网(IoT)的消息队列协议,它是 ISO 和 OASIS 标准,当前支持 MQTT 的最著名的中间件是 Eclipse Mosquitto 和 HiveMQ。
优点:
-
轻量
-
双向
缺点:
-
不适用于与物联网无关的微服务
-
功能不够丰富
Redis
RESP - Redis Serialization Protocol 是 Redis 的协议。Redis 是一个基于内存的 Key-Value 数据库。从技术上讲,Redis 不是一个消息队列中间件,但通过一些客户端手段,Redis 可以用于异步消息通讯。这主要是那些喜欢使用 Redis 的开发者或者一些简单场景采用的手段,因此,也把 Redis 纳入消息中间件。
优点:
-
Redis 基于内存,速度相当快
-
对于已经使用 Redis 的人来说,基本无学习曲线
缺点:
- 消息没有持久化,有丢消息的风险
- 功能不够丰富
- 不适用于其他协议能解决的所有应用场景
云服务,如 AWS SQS
如果不想自己维护基础设施,并需要自动扩容,或者本身就在公有云上,此时,可以考虑直接使用云服务。
优点:
-
方便快捷
-
稳定,自动扩容
-
无需个人维护
缺点:
-
对于简单或轻量的业务场景 ,费用可能过高
-
客户端兼容问题依赖云厂商解决,对某些开发语言可能兼容不好
-
非客户端交互模式,如 API 方式可能存在性能问题
如何选择消息队列
总之,一般情况下使用 AMQP - RabbitMQ 或 Kafka,对消息可靠性要求较高时考虑 RabbitMQ,否则选择 Kafka。
请勿采用 STOMP,因为它没有被很好的兼容,而且没有很好的优化。
如果您在从事物联网业务,那么请使用 MQTT,它适合物联网。
如果您喜欢 Redis,并且无法添加其它新技术,那么可以继续使用 Redis,但需要接受 Redis 在消息队列中的缺点。
如果您本身就在使用公有云,具备一定的用户或业务体量,对消息队列有稳定性及自动扩容的要求,并且能接受其费用,那么,选择云服务提供的消息队列。