阅读全文,约 15 分钟
这是江帅帅的第018篇原创
一、消息中间件
1.1 消息
首先,我们先看什么是消息?
消息,一般由生产者创建,指的是在应用之间传递的数据,也可以说是一个带有业务逻辑结构的数据。一般分为两部分:消息体(payload)和标签(label)。比如,有字符串、JSON 等不同类型的数据。
1.2 消息中间件
然后,再来看什么是消息中间件?
消息中间件,指的是利用高效可靠的消息传递机制,进行与平台无关的数据交流,并基于数据通信来进行分布式系统的继承。
主要有两种传递模式:P2P 点对点 模式和 Pub/Sub 发布/订阅 模式。
1)点对点模式:是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息,队列的存在使得消息的异步传输成为可能。
2)发布/订阅模式:定义了如何向一个内容节点发布和订阅消息,这个内容节点称为主题(相当于一个消息传递的中介),消息发布者将消息发布到某个主题上,消息订阅者则从主题中订阅消息。
1.3 简单原理
面向消息的中间件(MOM,Message Oriented Middleware)提供了以松散耦合的灵活方式继承应用程序的一种机制。常见的有 RabbitMQ、ActiveMQ、RocketMQ、KafKa 这几种。
原理很简单,主要提供了基于存储和转发的应用程序之间的异步数据发送,也就是应用程序之间不直接通信,而是与作为中介的消息中间件通信。更好的一点是,它提供了有保证的消息发送,应用程序开发人员无须了解 RPC(远程过程调用)和网络通信协议的细节。
消息中间件适用于需要可靠的数据传送的分布式环境。
采用消息中间件的系统中,不同对象之间通过传递消息来激活对方的事件,以完成相应的操作。发送者将消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接受者。消息中间件能在不同平台之间通信,它常被用来屏蔽各种平台及协议之间的特性,实现应用之间的协同,其优点在于能够在客户和服务器之间提供同步和异步的连接,并且在任何时刻都可以将消息进行传送或者存储转发,这也是他比远程过程调用更进步的原因。
1.4 常见作用
1. 解耦
在处理过程中间插入基于数据的接口层,两边的处理过程都要实现这一接口,可独立地扩展或修改两边的处理过程,确保遵守同样的接口约束即可。
2. 冗余(存储)
开发中避免不了数据在处理的过程中会失败,所以我们需要将数据进行持久化,直到被完全处理,减少和规避此类风险。删除之前,也需确保已经被完整处理。
3. 扩展性
因为消息中间件解耦了应用的处理过程,当想提高消息入队和处理的效率,不需要改变代码,不需要调节参数,只要增加处理过程即可。
4. 削峰
首先,峰代表的是访问量激增。激增的情况不常见,不能作为标准方案去调用资源,会显得非常浪费。使用消息中间件是一个很好的解决方案,它能够使关键组件支撑突发访问压力,不会因为突发的超负荷请求而完全崩溃。
5. 可恢复性
万一碰上某些组件失效时,也不会影响到整个系统。因其降低了进程之间的耦合度,如果有某个处理消息的进程挂掉了,加入的消息还是可以在系统恢复之后再度处理的。
6. 顺序保证
数据处理的顺序很重要。
7. 缓冲
通过缓冲层来帮助任务最高效率地执行,写入的处理尽可能快速,有助于控制和优化数据流经过系统的速度。
8. 异步通信
有时候,如果不想也不需要立即处理消息,则通过其异步处理机制,允许应用将一些消息放入消息中间件中,需要的时候再来处理。
二、RabbitMQ
2.1 RabbitMQ 概述
RabbitMQ 是采用 Erlang 语言编写的基于 AMQP 协议的开源消息代理服务器(message broker)。
无论你的应用是位于云端,还是 位于企业自身的数据中心,RabbitMQ 是一种功能非常强大、轻量级的工具,常用来构建十分简单或者异常复杂的分布式软件架构。
RabbitMQ 主要是用来实现应用程序的异步和解耦的一个消息队列技术,同时也能起到消息缓冲,消息分发的作用。在实际开发架构中,扮演着 消息中间件MOM 的角色。
消息中间件核心作用是解耦,主要用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。简单理解就是,消息的发送者无需知道消息使用者的存在。
在分布式的系统中,消息队列也会被用在分布式事务,RPC 的调用等等。
RabbitMQ 具有高可用性、高性能、灵活性等一些特点收割了大批开发者。
2.2 使用场景
1. 常见异地跨系统的异步通信
最常见的,比如有些金融机构对接央行系统,不可能使用同步阻塞通信,它们需要连接城市处理中心或者国家处理中心等。因为有太多的商业银行或其他金融机构,我们需要将消息发布到 MQ 中,然后等待应答。
2. 秒杀场景
我们可以将前端发送到 Controller 中的请求,当作一个消息发送到消息队列中,避免直接访问数据库,再由业务层去消息队列中取出并消费消息。
3. 发布订阅场景
比如我们的付款、退货成功等,都可以使用 pub/sub 模型实现的事件驱动来实现。
如果我们的项目非常庞大的时候,就需要根据不同的表,对应拆分成不同的系统模块了,比如会员系统、订单系统、商品系统等等。那么,相互之间访问的话就变成了跨系统访问,在这个时候最好是相互之间都存放到对应的一套基础数据信息,而不是直接去调用 API 访问,这就需要我们进行同步数据。
2.3 RabbitMQ 的特性
1. 开源
目前隶属 Pivotal Software 公司,并且以 Mozilla 公共协议进行发布。
2. 可靠性
使用一些机制来保证它的可靠性,如持久化、传输确认、发布确认。
3. 灵活的路由
在消息进入队列之前,通过 Exchange 来路由消息的。
对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
4. 消息集群
多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。
另外,它的高延迟消息通信的平衡,我们也了解一下。因为不是所有的网络拓扑和架构都是一样的,它不仅支持在低延迟环境下的消息通信机制,也提供了针对互联网的高延迟环境下的插件。
那么,它就可以在同一个本地网络或者在跨多个数据中心的共享互联机制下构建消息集群。
5. 高可用
队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
在这里,我们也可以体验到它的一个亮点,主要是针对不同的场景。在消息吞吐量和性能上,它提供了一种灵活性用于控制这两种在可靠消息通信上的平衡。消息在投递之前可以指定为是否持久化到硬盘;在集群中,我们一般要设置为高可用,那消息就会被存储在多服务器中来保证出问题时可恢复。
6. 多层安全
客户端连接可以通过使用 SSL 通信和客户端证书验证以提高安全性。
在虚拟主机层,可以管理用户访问,从而在较高层次实现消息和资源的隔离。
通过配置可以使用正则表达式模式匹配的方式,控制从队列中读取消息和把消息写入交换机的过程。
使用插件可与类似 LDAP 的外部认证系统进行集成。
7. 多平台的客户端使用
RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 、Python、PHP、JavaScript 等等。
它提供的客户端开发库就是为了支持常几乎所有的编程语言并能运行于多个平台上,这是它比较吸引人的地方。通过编程与 RabbitMQ 进行通信的时候,不会受任何供应商或开发语言的限制。
8. 轻量级
RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面。运行 RabbitMQ 核心功能以及诸如管理界面的插件只需要不到 40MB 内存。但如果你往队列中添加消息就有可能会增加其内存使用量。
9. 跟踪机制
如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
10. 插件机制
RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。比如,当我们需要进行数据库直接写入的时候,可以通过插件把消息直接存到数据库中。
2.4 为什么是 Erlang 语言?
需要来认识下 Erlang,它主要是面向电信行业的函数式编程语言。它被设计成一种分布式、高容错的软实时系统,用于构建 99.999% 可用性的应用系统。它专注于节点之间消息通信的轻量级进程,提供了状态无关的高并发性。
消息代理服务器作为一种应用程序,维护并发连接、实现消息路由并管理它们的状态,Erlang 本身就基于并行处理和消息通信处理,所以非常合适。
另外,Erlang 的分布式通信架构用在构建 RabbitMQ 集群机制上也是非常完美的。RabbitMQ 集群中的服务器充分利用 Erlang 的进程间通信系统(Inter-Process Communication,IPC),比其他竞品强悍多了。
RabbitMQ 集群使用 Erlang 自带的进程间通信机制实现虚拟机之间的跨节点通信、共享状态信息以及允许整个集群内进行消息的发布和消费,如下图所示。
2.5 AMQP 协议
先从消息队列(Message Queue)开始说起,起源于金融行业系统,最初设计成类似主板上的总线那样,允许其他程序接入。允许开发者根据一系列规则去描述消息的内容,只要消息遵守建立的规则发布,其他消费者都可以去订阅感兴趣的内容。
这一亮点在金融交易应用中的反响特好,吸引了 BIM、微软等公司相继推出 BIM MQ、MSMQ 等产品,但相互之间无法通信。直到 AMQP 推出之后颇受欢迎,因为它一开始就设计为开放标准,任何人都可以执行这一标准,让交互通信成为可能。
AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是一个提供统一消息服务的应用层标准高级消息队列,主要是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端、中间件同产品、不同的开发语言等条件的限制。
AMOP 协议自身降低耦合的机制是基于与上层产品、语言无关的协议。它是一种二进制协议,提供客户端应用于消息中间件之间多通道、协商、异步、安全、中立和高效地交互。
AMQP 规范可以拆分成两部分,不仅定义了与 RabbitMQ 交互的点对点协议,叫传输层;也提供了描述 RabbitMQ 核心功能的逻辑模型,叫功能层。
传输层 Transport Layer :基于二进制数据流传输,用于将应用程序调用的指令传回服务器,并返回结果,同时可以处理信道复用、帧处理、内容编码、心跳传输、数据传输和异常处理。
功能层 Functional Layer:位于协议上层,主要定义了一组命令(基于功能的逻辑分类),提供了关于每个类、方法、属性和字段的多层次的详细信息,用于应用程序调用实现自身所需的业务逻辑。
但在深入研究的时候,需要知道 AMQP 有多个版本,RabbitMQ 内核架构比较接近 AMQP 0-8 和 AMQP 0-9-1 版本。
AMQP 的具体实现有:RabbitMQ、OpenAMQ、Apache Qpid、Redhat Enterprise MRG、AMQP Infrastructure、øMQ、Zyre 等。
2.6 松耦合架构
消息队列最重要的三个作用:异步、解耦、削峰。
- 跨系统的异步通信(银行 IBM MQ)
- 应用内的同步编程异步(秒杀)
- 基于 Pub / Sub 模型实现的事件驱动(付款成功、退款成功、多系统同步数据)
- 利用 RabbitMQ 实现事务的最终一致性
举例:我们在某商城买一款商品,当我们下单购买完成时,库存系统应该对应将其库存量值减少 。
在传统做法中,我们订单提交之后,同时需要将库存进行减少,这个操作具有原子性,需要保证在同一个事务当中;诸如采用此类方式,在数据表不是太多的情况下,处理时间并不会太长,但这有隐患;性能问题逐渐滋生的原因在于这些数据库更新采用的是串行方式,每一个操作都需要在前一个操作完成之后才能开始。
但在目前高并发的情形中,并不会这么处理,因为效率非常低,应用系统之间的耦合度也非常高。我们需要使用消息队列来解决此类问题,因为它们可以把消息分散到任意数量的消费者应用程序中,然后由这些消费者应用程序执行数据库的操作。
比如,客户下单后,我们应该将订单信息发送给消息队列代理服务器的服务队列中;然后库存系统可以从消息队列中获取信息来处理,这样就可以将系统之间的耦合度进行拆分,同时其消息机制的异步处理机制也能提高我们的效率,通过配合集群使用,即可处理并发访问等。
我们可以通过向基于 RabbitMQ 的松耦合设计应用架构中迁移,可以在不需要改动其他任何核心应用的前提下,加入新的应用(功能),因为它可不以数据为中心,不再受限于数据库写入的性能瓶颈。
我们推荐松耦合架构,就像这样将数据库从应用中解耦出来。RabbitMQ 以一个中介的身份,肩负处理入库之前的操作。消费者从 MQ 服务器中获取到数据,然后执行与数据库相关的操作。
松耦合的亮点,主要是能够把数据发送到 RabbitMQ,实现异步数据处理。
那它是怎么添加新功能的呢?非常简单。RabbitMQ 允许重复利用同一份数据,所处理的消息会有副本,同一份数据可以发送到新的云服务和新的数据库上,RabbitMQ 将它们路由到多个消费者,消费者直接使用对应的数据去处理不同的目标即可。
RabbitMQ 还支持应用之间的互联化消息投递和同步,应用互联插件,消息可以在多个数据中心之间进行复制并执行一样的任务。
我们改造一下,给数据中心 2 也添加一个应用程序,这样 RabbitMQ 就能双向接收互联数据,基于此可创建分布在不同物理位置的高可用应用,通过这种方式为应用提供了与地域性无关的水平扩展,同时也提供了一种高性价比的基础设施实现分布式处理解决方案。