MQ学习小记

“消息队列”是在消息的传输过程中保存消息的容器。

“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。

消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。

一般的消息队列是基于生产者-消费者模型。用于实时性要求并不是那么高、异步通信的场景。

AMQP模型

AMQP(AdvancedMessageQueuingProtocal,高级消息队列协议)是一个提供统一异步消息传递服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件而设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品、不同开发语言等条件的限制。

AMPQ的消息模型示意

运作过程

左边的客户端向右边的客户发送消息,流程如下:

  1. 获取Connection(客户端到MQ服务器的TCP链路)
  2. 获取Channel(逻辑层的链路,基于Conncetion)
  3. 定义交换器、队列
  4. 使用一个RoutingKey将队列绑定到一个交换器
  5. 通过指定一个交换器和一个RoutingKey来消息发送到对应的队列上
  6. 接收方在接受时也是获取Connection,接着获取Channel,然后指定一个队列直接到它关心的队列上取消息,它对交换器、RoutingKey及如何绑定都不关心,到对应的对列上取消息就行了

名词解释

在该模型中,三个主要功能模块连接成一个处理链完成预期的功能:

  • exchange(交换器):接收发布应用程序发送的消息,并根据一定的规则将这些消息路由到“消息队列”。
  • message queue(消息队列):存储消息,直到这些消息被消费者安全处理完为止。
  • binding(绑定器):定义了exchange和message queue之间的关联,提供路由规则。

Exchange本身不保持消息,只是起到路由的作用,Exchange接收消息生产者(MessageProducer)发送的消息根据不同的路由算法将消息发送往MessageQueue。MessageQueue会在消息不能被正常消费时缓存这些消息,具体的缓存策略由实现者决定,当MessageQueue与消息消费者(Messageconsumer)之间的连接通畅时,MessageQueue会将消息转发到consumer。

一个Broker(AMQP服务器)中会存在多个MessageQueue?Exchange怎样知道它要把消息发送到哪个MessageQueue中去呢?图8-1中的Binding就是通过绑定Exchange与MessageQueue来解决这个问题。消息应用者(ClientApplication)控制Exchange与某个特定MessageQueue绑定,并将这个MessageQueue接受何种特定消息的条件绑定到Exchange,这个条件也叫Bindingkey或Criteria。AMQP协议的架构如下图示意。

AMQP架构图

该图(VirtualHost)用来指Exchange和MessageQueue组成的集合。它是一个虚拟概念,一个虚拟主机可以是一台服务器,还可以是由多台服务器组成的集群,还可以是一些虚拟机组成的集群,上面运行一些Exchange和MessageQueue。

下面详细解释一下AMQP的工作原理:

从上图看,Message是AMQP所操纵的基本单位,它由Producer产生,经过Broker被Consumer所消费。它的基本结构有两部分:Header和Body。Header是由Producer添加上的各种属性的集合,这些属性有:控制Message是否可被缓存,接收的Queue是哪个,优先级是多少等。Body是真正需要传送的数据,它是对Broker不可见的二进制数据流,在传输过程中不应该受到影响。

一个Exchange在与多个MessageQueue通过绑定后,Exchange中就会存在一个路由表,这个表中存储着每个MessageQueue所需要消息的限制条件。Exchange就会检查它接收到的每个Message的Header及Body信息,来决定将Message路由到哪个Queue中去。每个Message的Header中应该有个属性叫RoutingKey,它由Message发送者产生,提供给Exchange路由这条信息的标准。Exchange根据不同路由算法有不同有ExchangeType。

  1. Direct类型,需要BindingKey等于RoutingKey。也就是1对1模式。
  2. Topic类型,所有符合RoutingKey(可以是一个表达式)RoutingKey所绑定的队列可以接受消息。相当于通过RoutingKey进行了过滤。
  3. Fanout类型,忽略BindingKey和RoutingKey,消息传递到所有绑定的MessageQueue。即所有绑定此交换器的队列都可以接收消息,典型的订阅/发布模型。
  4. 也可以根据Message包含的某些属性来判断。

这些基础的路由算法由AMQP提供,当然ClientApplication也可以自定义各种自己的扩展路由算法。

当Exchange按照一定的路由算法把消息发到MessageQueue后,作为消息的存储和分发实体,MessageQueue会把消息缓存到内存或硬盘中,并且按照顺序把这些消息发给一个或者多个消息的消费者。

MQ在实际项目中的使用

openStack中的MQ

OpenStack遵循这样的设计原则:项目之间通过RESTful API进行通信;项目内部,不同服务进程之间的通信,则必须要通过消息总线。这种设计思想保证了各个项目对外提供服务的接口可以被不同类型的客户端高效支持,同时也保证了项目内部通信接口的可扩展性和可靠性,以支持大规模的部署。

软件从最初的面向过程,面向对象,再到面向服务(SOA),要求我们去考虑各个服务之间如何传递消息。借鉴硬件总线的概念,消息总线的模式被引入,顾名思义,一些服务向总线发送消息,其他服务从总线上获取消息。

OpenStack oslo.messageing库实现了以下两种方式来完成项目内部各服务进程之间的通信:

远程过程调用(RPC,Remote Procedure Call)

通过远程过程调用,一个服务进程可以调用其他远程服务进程方法,并且有两种调用方式:call和cast。call 则是同步执行的,调用者会被阻塞直到结果返回;cast 则是异步执行,结果不会立刻被返回,调用者也不会被阻塞,但是调用者需要利用其他方式查询这次远程调用的结果。

事件通知(Event Notification)

某个服务进程可以把事件通知发送到消息总线上,该消息总线上所有对此类事件感兴趣的服务进程,都可以获得此事件通知并进行一步的处理,处理的结果并不会返回给事件发送者。这种通信方式,不但可以在同一个项目内部的各个服务进程之间发送通知,也可以实现跨项目之间的通知发送。Ceilometer就通过这种方式大量获取其他OpenStack项目的事件通知,从而进行计量和监控。

OpenStack中的通信方式

AMQP

OpenStack中所支持的消息总线类型中,大部分都是基于AMQP的。前面已经提到过了,故不赘述。

基于AMPQ实现RPC

配图解释:

openStack设计与实现——基于AMPQ的RPC实现.jpg

  • 客户端发送一个请求消息给Exchange,指定routing key为"op_queue",同时指明一个消息队列名用来获取响应,图中为"res_queue",同时指明一个消息队列名用来获取响应。在图中为"res_queue"
  • Exchange把此消息转发到消息队列op_queue
  • 消息队列op_queue把消息推送给服务端,服务端执行此RPC调用对应的任务。执行结束后,服务端把相应结果发送给消息队列,指明routing key为"res_queue"
  • Exchange 把此消息转发到消息队列res_queue
  • 客户端从消息队列res_queue中获取响应。

小结

对于MQ中的概念进行了简单的介绍。之后还会进行一定的扩充——比如在一些常见应用中MQ的应用。

扩展参考

阅读 1.7k

推荐阅读
泊浮说
用户专栏

作者是个热爱分享交流的人,所以有了这个专栏。你的点赞是我最大的更新动力。

56 人关注
45 篇文章
专栏主页
目录