消息中间件产品知识

消息队列(Message Queue)已然发展成为云计算中间件的重要组件,它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列特点。 本文主要站在产品角度,探讨了消息队列常用的术语、应用场景以及成熟的中间件解决方案。最后以 AWS 为例,分析了云计算平台的消息中间件产品。

消息中间件产品知识

基本概念和简单原理

消息队列(Message Queue)是实现进程间或同一进程的不同线程间通信的方式。消息队列提供了异步的通信实现,发送者和使用者无需知道彼此的位置信息,即可独立处理消息。队列中的消息来自于发送者,可以驻留在内存或磁盘上,直到被使用者消费。 消息队列中间件是分布式系统的重要组件,已经逐渐成为企业 IT 系统内部通信的核心手段,它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,是异步 RPC 的主要手段之一。

消息队列常用术语

Broker 概念来自于 Apache ActiveMQ,可以理解为消息队列服务器,主要实现消息路由和暂存等功能

消息生产者 Producer 向队列发送消息的应用程序

消息消费者 Consumer 从队列接收消息的应用程序

点对点模型 P2P 也可称作“生产消费模式”,在该模型下由生产者向队列发送消息,消费者从队列接收消息,并会在成功处理后返回消费签收确认(Acknowledge),系统可以有多个消费者,但每条消息只能被一个消费者消费,消息一旦被消费,便不再存在队列中。生产者和消费者没有时间上的依赖关系,生产者发送消息后,无论消费者有没有在运行,都不影响消息的后续消费。

P2P 模型特点:单一消费者、消费应答机制、无时间依赖关系

发布者 Publisher 在发布订阅模型中,对消息生产者的称呼

订阅者 Subscriber 在发布订阅模型中,对消息消费者的称呼

话题 Topic 在发布订阅模型中,用于实现发布者和订阅者消息交换的标识,所有订阅了 Topic 的订阅者都可以收到对应的 Topic 消息

发布订阅模型 Pub/Sub 也可称作“观察者模式”,支持了发布者和多个订阅者通过特定的话题(Topic)实现数据交换,当发布者发布(Publish) 一条 Topic 信息时,所有订阅(Subscribe)了该话题的订阅者都会接收到这条消息。消费者必须先订阅后才能消费发布者的消息,为了消费消息,订阅者必须保持运行的状态。

Pub/Sub 模型特点:多个消费者、无消费应答、有时间依赖关系

消息的顺序性保证 生产消费模式下,利用 FIFO 先进先出的特性,可以保证消息的顺序性

消息的 ACK 确认机制 生产消费模式提供了消息的 Acknowledge 机制,可以保证消息不丢失。当消费者确认消息处理完成后,发送一个 ACK 给消息队列,此时消息队列可以删除这个消息。如果消费者宕机/关闭,没有发送 ACK,消息队列将会把消息发送给其他消费者重新处理

消息的持久化 消息的持久化,对于一些关键的核心业务来说是非常重要的,启用消息持久化后,消息队列宕机重启后,消息可以从持久化存储恢复,消息不丢失,可以继续消费处理

消息的同步收发 消息同步发送时,发送方需要等待接收方的 ACK 确认,在收到 ACK 之前发送方将一直处于阻塞状态;消息接收方以同步方式(Pull)接收时,如果队列中为空,此时接收将处于同步阻塞状态,会一直等待,直到消息的到达

消息的异步收发 消息异步发送时,发送方不需要等待 ACK 确认;异步接收消息,消息队列以 Push 方式触发消费者接受消息

消息的事务支持 消息的收发处理支持事务管理,在任务中心场景中,一次处理可能涉及多个消息的接收、处理,这处于同一个事务范围,当事务中一个消息处理失败时,事务回滚,消息重新回到队列中

应用场景

提高吞吐量

在特定的一些业务场景下,消息队列的异步处理机制,可以降低业务请求的响应时间,提高吞吐量。

场景举例:用户注册流程,注册成功后,发送注册邮件和注册短信

传统串行处理 传统串行处理 传统并行处理 传统并行处理 引入消息队列,将一部分非必须的业务逻辑采用异步处理,改造后的架构如下: 消息队列-提高吞吐 通过将发送邮件、发送短信功能的异步处理,可以将注册流程的响应时间提升到 55ms。

假设 CPU 每秒的吞吐量是 100 次,则传统串行方式 1 秒内 CPU 可处理的请求量 6 次(1000/150),传统并行方式是 10 次(1000/100),异步处理则可以提升到 18 次(1000/55)。

业务解耦

从上例应用可以看出,消息队列的异步处理机制能够有效的实现不同业务功能之间的解耦合。举例说明,在电子商务领域,常使用消息队列来实现 订单系统库存系统 的解耦。业务系统的解耦合有很多好处,通俗来讲可以有效避免由于库存系统宕机等异常情况而导致的订单系统功能失败的情况。 消息队列-业务解耦

流量削峰

在业务解耦的同时,利用队列的容量上限,可以实现对激增流量的削峰处理。我们知道,在面对激增的访问请求时,上下游的处理能力是不一样的。例如,Web 前端可以通过 Nginx 搭配负载均衡设备实现每秒上千万的并发请求,但数据库的处理能力却非常有限,SSD 加分库分表的单机处理能力只在万级别。

因此在有激增流量的场景下,为了避免激增的请求导致应用宕机,通常会采用消息队列,实现上下游系统的内容转储。例如在秒杀或团抢活动,应用非常广泛,主要有以下几点好处:

  • 可以控制活动人数。用户的请求服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求并跳转到失败页面
  • 可以缓冲瞬间激增的高流量
  • 可以实现用户请求和订单业务系统的异步处理

广播

利用 Pub/Sub 模式,可以轻松实现消息的广播。在传统实现下,若不采用消息队列,每当接入一个新的业务方,势必需要进行一次接口联调,降低效率也徒增系统的不稳定性。引入消息队列后,消息生产者只需要确保消息准确送达到队列,下游的消费者自行维护订阅即可。 消息队列-广播 使用时,注意 Pub/Sub 模式的时间依赖性

最终一致性

最终一致性指的是两个系统的状态保持一致,要么都成功、要么都失败。是分布式系统的数据同步、金融/电商领域资金流转系统的重要考虑点。

利用消息队列实现最终一致性的方法可以简要描述为:主要利用“记录”和“补偿”的方式,在事情成功完成之前,始终记录事情的详情,直到收到接收方的“成功”应答后,再清理掉事情记录。对于失败和超时情况,则依靠定时任务实现重复调取,直到成功为止。

具体来说,本地事务维护业务变化和通知消息,一起落地(失败则一起回滚),然后 RPC 到达 Broker,在 Broker 成功落地后,RPC 返回成功,本地消息可以删除。否则本地消息一直靠定时任务轮询不断重发,这样就保证了消息可靠落地 Broker;Broker 往 Consumer 发送消息的过程类似,一直发送消息,直到 Consumer 发送消费成功确认。 通过两次消息落地加补偿,下游是一定可以收到消息的,然后依赖 RPC 版本号等方式做判重,更新自己的业务,就实现了最终一致性。

常用的消息中间件

JMS ( Java Message Service ) API 作为消息服务的规范,可以在商用的容器上(如 WebLogic、JBoss)直接使用开发。此次讨论范围仅限于 Active MQ、Rabbit MQ、Kafka 三类云计算常用的消息中间件产品。

ActiveMQ

ActiveMQ 是 Apache 出品,能力强劲的开源消息总线,是完全支持 JMS1.1 和 J2EE 1.4 规范的 JMS Provider 实现。 ActiveMQ 的主要特性有:

  • 多种语音和协议编写客户端,语言:Java, C, C++, C#, Ruby, Perl, Python, PHP。应用协议:OpenWire, Stomp REST, MQTT, XMPP, AMQP, WS Notification
  • 对 Spring 的支持,ActiveMQ 可以很容易内嵌到使用 Spring 的系统里面去,而且也支持 Spring2.0 的特性
  • 从设计上保证了高性能的集群,客户端-服务器,点对点
  • 支持多种传送协议:in-VM, TCP, SSL, NIO, UDP, JGroups, JXTA

RabbitMQ

RabbitMQ 是基于 Erlang 语言编写的开源消息队列,是 AMQP 的标准实现。支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持 AJAX,持久化。RabbitMQ 实现了代理(Broker)架构,消息在发送到客户端之前可以在中央节点上排队,用于在分布式系统中存储转发消息,在适宜于路由、负载均衡、消息持久化等场景。但相应的因为中央节点而增加了延迟,以及消息封装后也比较大,导致 RabbitMQ 的速度较慢。Erlang 环境也使得可扩展性不好。 场景应用:记账重试失败、通知服务等,消息不允许丢失的条件下使用

Kafka

Kafka 是 LinkedIn 于2010年12月开发并开源的一个分布式流平台,现在是Apache的顶级项目,它是一种 高性能跨语言的分布式 Pub/Sub 消息系统,它可以处理客户在网站中的所有动作流数据(如网页浏览、搜索以及其他用户的行为)。由于吞吐量的限制要求,这些数据通常是通过处理日志和日志聚合来获取的。 对于像 Hadoop 的一样的日志数据离线分析系统,又要求实时处理,这是一个可行的解决方案。Kafka 的目的是通过 Hadoop 的并行加载机制来统一线上和离线的消息处理,也是为了通过集群机来提供实时的消费。

作为高吞吐量的分布式发布/订阅消息系统,Kafka 有如下特性:

  • 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于 TB 级别的消息也能够保持长时间的稳定存储(以文件追加的方式写入数据,过期的数据定期删除)。
  • 高吞吐量:Kafka 可以支持每秒数百万并发数
  • 支持通过 Kafka 服务器和消费机集群来分区消息
  • 支持 Hadoop 并行数据加载 场景应用:一般应用在大数据日志处理,或对实时性、可靠性要求稍低的场景(可能会有少量延迟和丢数据)

Amazon 消息队列产品介绍

Amazon MQ

一种适用于 Apache ActiveMQ 的托管消息代理服务,Amazon MQ 负责管理和维护 ActiveMQ 其底层基础设施经过自动预置,具备高可用性和消息持久性,能够保证应用程序的可靠性。

支持行业标准 API 和协议,适用于产品中已经使用了消息队列,而希望将 Broker 上云的客户

Amazon SQS

Amazon Simple Quene Service 是一款 Web 服务,提供了应用程序接入消息队列的功能。Amazon SQS 所提供的可靠且可扩展的托管队列,为构建解耦合的分布式应用程序提供便利。

适用于在云中构建全新的应用程序,SQS 是一种采用 P2P 模式的消息队列,提供了无时间依赖关系的消息队列

Amazon SNS

Amazon Simple Notification Service 是一种让用户能够轻松构建、维护并从云中发送通知的 Web 服务。该服务旨在让开发人员能更轻松的进行互联网规模计算。

适用于在云中构建全新的应用程序,SNS 是一种采用 Pub/Sub 模式的消息队列,提供了无需轮询的消息队列

个人理解

  1. Amazon MQ 产品主要解决用户已有产品的消息队列代理服务移植上云的需求,利用分布式系统提供可以扩展的计算资源。因此产品的主要关注性能就是兼容性和可托管。这也正是产品基于 ActiveMQ 而非 Kafka 或 RabbitMQ 的原因(ActiveMQ 支持多种开发语言和协议,也更适合高性能的集群)
  2. 对于可以从零开始,开发产品的客户,Amazon 提供了 SQS、SNS 的消息队列服务
  3. SQS 和 SNS 两款产品分别采用了点对点模式和发布/订阅模式的消息队列,就可以满足不同业务需求的客户。对于客户来讲,这是两款开箱即用的消息队列产品,同时也能更好的与 AWS 其他产品搭配使用,比如和 Lambda、Cloud Watch等。

参考资料