什么是消息中间件?
定义一:消息(Message)是指在应用间传送的数据。
定义二:消息队列中间件(Message Queue Middleware,MQ)是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
目前开源的主流消息中间件包括:RabbitMQ、Kafka、ActiveMQ、RocketMQ等。
消息队列中间件(也可以称为消息队列或者消息中间件)一般有两种传递模式:点对点模式(P2P)和发布/订阅模式(Pub/Sub)。
· 点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列接收消息,队列的存在使得消息的异步传输成为可能。
· 发布/订阅模式定义了如何向一个内容节点发布和订阅消息,这个内容节点称为主题。主题可以认为是消息传递的中介,消息发布者将消息发布到某个主题,而消息订阅者则从主题中订阅消息。主题使得消息的订阅者与消息的发布者互相保持独立,不需要进行接触即可保证消息的传递,发布/订阅模式在消息的一对多广播时采用。
面向消息的中间件(Message Oriented Middleware,MOM)提供了以松散耦合的灵活方式集成应用的一种机制:
它们提供了基于存储和转发的应用程序之间的异步数据的发送,即应用之间彼此不直接通信,而是与作为中介的消息中间件通信,消息中间件提供了有保证的消息发送,应用程序开发人员无须了解远程过程调用(RPC)和网络通信协议的细节。
消息中间件适用于需要可靠数据传输的分布式环境,采用消息中间件的系统中,不同对象之间通过传递消息来激活对方的事件完成相应操作。发送者将消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接收者。
消息中间件能在不同平台之间通信,常被用来屏蔽各种平台及协议之间的特性,实现应用程序之间的协同。其优点在于能够在客户和服务器之间提供同步和异步的连接,并且在任何时刻都可以将消息进行传送或者存储转发,这是它优于远程过程调用的原因。
消息中间件的作用
解耦:消息中间件在处理过程中插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许开发者独立地扩展或修改两边的处理过程,只要确保遵循相同的接口约束即可。
冗余(存储):在某些情况下处理数据的过程会失败,消息中间件可以把数据进行持久化知道完全被处理。通过这一方式规避了数据丢失风险。在把一个消息从消息中间件删除之前,需要处理系统明确地支出该消息已经被处理完成,从而确保数据被安全的保存直到使用完毕。
扩展性:消息中间件解耦了应用处理过程,提高消息入队和处理效率只需要增加额外的处理过程,不需要其他更改。
削峰:消息中间件可以使得关键组件支撑突发访问压力,不会因为突发的超负荷请求而完全崩溃。
可恢复性:当一部分失效时,不会影响到整个系统。消息中间件降低了进程间的耦合度,即使一个处理消息的进程挂掉,加入消息中间件的消息仍然可以在系统恢复后进行处理。
顺序保证:在大多数使用场景下,数据处理的顺序很重要,大部分消息中间件支持一定程度上的顺序性。
缓冲:在任何重要的系统中,都会存在不同处理时间的元素。消息中间件通过一个缓冲层来帮助任务最高效率的执行,写入消息中间件的处理会尽可能快速。该缓冲层有助于控制和优化数据流经过系统的速度。
异步通信:消息中间件提供了异步处理机制,允许应用把一些消息放入消息中间件中,但不立即处理它,再之后需要的时候再慢慢处理。
相关概念介绍
生产者(Producer):就是投递消息的一方。
生产者创建消息然后发布到RabbitMQ中。消息一般可以包含两个部分:消息体(PayLoad)和标签(Label),在实际应用中消息体一般是一个带有业务逻辑结构的数据,比如一个JSON字符串,当然也可以进一步对消息体进行序列化操作。消息的标签用来表述这条信息,比如一个交换器的名称和一个路由键。生产者把消息交给RabbitMQ,RabbitMQ根据标签把消息发送给感兴趣的消费者(Consumer)。
消费者(Consumer):就是接收消息的一方。
消费者连接到RabbitMQ服务器,并订阅到队列上。当消费者消费一条信息时只是消费了消息的消息体(PayLoad),在消息路由的过程中消息的标签会丢弃,存入到队列的消息只有消息体,消费者也只会消费到消息体,也就不知道消息的生产者是谁(也不需要知道)。
服务节点(Broker):消息中间件的服务节点。对于RabbitMQ来说,一个RabbitMQ Broker可以看作一个服务节点(服务实例),大多数情况下也可以将其看作一台RabbitMQ的服务器。
首先生产者将业务方数据进行包装封装成消息,发送(在AMQP协议里命令为Basic.Publish)到Broker中,消费者订阅并接收消息(在AMQP协议里命令为Basic.Consume或Basic.Get),经过解包处理得到原始数据再进行业务处理逻辑。这个业务的处理逻辑不一定和接收消息的逻辑使用同一个线程,消费者进程可以使用一个线程去接收消息存入内存(如Java中的BlockingQueue),业务逻辑使用另一个线程从内存读取,进一步解耦应用,提高处理效率。
队列(Queue):是RabbitMQ的内部对象,用于存储消息。RabbitMQ中消息只能存储在队列中,这一点和Kafka相反。Kafka将消息存储在topic(主题)这个逻辑层面,而相对应的队列逻辑知识topic实际存储文件中的位移标识。
RabbitMQ的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会通过轮询策略平均分摊给多个消费者进行消费,而不是每个消费者都收到所有的消息进行处理。RabbitMQ不支持队列层面的广播消费,虽然可以二次开发实现功能但会使处理逻辑异常复杂,不建议这样做。
交换器(Exchange)、路由键(RoutingKey)、绑定(Binding):实际上生产者不会直接将消息投递到队列中,而是生产者将消息发送到交换器,由交换器将消息路由到一个或多个队列中(如果路由不到,可能会返回给多个消费者,也可能直接丢弃)。
RabbitMQ中的交换器有四种,每种类型的交换器有不同的策略。生产者将消息发送给交换器的时候,一般会指定一个路由键用来指定这个消息的路由规则,路由键需要和绑定键(BindingKey)联合使用才能生效。在交换器类型和绑定键固定的情况下,生产者可以通过指定路由键来决定信息流向哪里。在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的绑定键,但绑定键是否生效取决于交换器类型。
交换器类型
RabbitMQ常用的交换器类型有 fanout、direct、topic、headers 四种,AMQP协议里还提供了 System 和 自定义 两种类型。
fanout:会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。
direct:会把消息路由到那些BindingKey和RoutingKey完全匹配的队列中。
topic:与direct的匹配规则类似,但增加了模糊匹配。
我们将使用点号分割的字符串称为单词(适用于RoutingKey和BindingKey),在BindingKey中存在两种特殊字符 * 和 # 用于模糊匹配,其中 * 用于匹配一个单词 # 用于匹配多规格单词(也可以是0个)。
headers:不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。
在绑定队列和交换器时指定一组键值对,当发送消息到交换器时,RabbitMQ会获取到该消息的headers(即一个键值对的形式)对比其中的将支队是否完全匹配队列和交换器绑定时指定的键值对。如果完全匹配则消息会路由到该队列,否则就不会。headers类型的交换器性能很差且不实用,基本上不会被使用。
RabbitMQ的安装运行
· 安装Erlang:yum install erlang
· 安装RabbitMQ:yum install rabbitmq-server
· 启动RabbitMQ:rabbitmq-server
· 验证启动:rabbitmqctl status
· 验证集群:rabbitmqctl cluster_status
生产消息的工作流程
· 生产者连接到RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)
· 生产者声明一个交换器,并设置相关属性(类型、持久化等)
· 生产者声明一个队列,并设置相关属性(排他、持久化、自动删除等)
· 生产者通过路由键将交换器和队列绑定起来
· 生产者发送消息至RabbitMQ Broker,其中包含路由键、交换器等信息
· 相应的交换器根据接收到的路由键查找相匹配的队列
· 如果找到则将生产者发送的消息存入相应的队列中,如果没有则根据配置选额丢弃还是退回
· 关闭信道和连接
RabbitMQ的消息生产
消费消息的工作流程
· 消费者连接到RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)
· 消费者向RabbitMQ Broker请求消费相应队列中的消息,可能会设置相应的回调函数及一些准备工作
· 等到RabbitMQ Broker回应并投递相应队列中的消息,接收消息
· 消费者确认(ack)接收到的消息
· RabbitMQ从队列中删除相应已经被确认的消息
· 关闭信道和连接
RabbitMQ的消息消费
AMQP协议介绍
RabbitMQ是遵从AMQP协议的Erlang实现(RabbitMQ还支持STOMP、MQTT等协议),AMQP的模型架构和RabbitMQ的模型架构是一样的,RabbitMQ中的交换器、交换器类型、队列、绑定、路由键等都是遵循AMQP协议中的相应概念。
AMQP协议本身包括三层:
· Module Layer:协议最高层,主要定义了一些供客户端调用的命令,客户端可以利用这些命令实现业务逻辑。
· Session Layer:中间层,主要负责将客户端的命令发送给服务器,再将服务端的应答返回给客户端,主要为客户端与服务器之间的通信提供可靠性同步机制和错误处理。
· Transport Layer:最底层,主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示等