玩转RabbitMQ之三:理解消息通信中的概念

一、消息投递

        为了对消息投递有更深刻的理解,先说说传统的“快餐车模式”,传统的BS模式,Browser(浏览器)发起请求,Server(服务器)处理相应请求,客户端请求的数据来源于服务器端,就像一个顾客向快餐车发起购买快餐的请求,快餐来源于快餐车。

玩转RabbitMQ之三:理解消息通信中的概念_第1张图片
"快餐车模式"

       对于消息投递,现实生活中向邮局投递信件就是一个非常贴切的类比,你写了一封信,投递到邮局的邮箱里去,邮递员定期从邮箱里取走邮件,然后根据上面写的地址投递到你的女朋友手上,这个过程邮局没有对你的信件做什么特别的处理,你的女朋友收到信之后会怎么处理也与邮局无关,这里邮局只是起到邮件传递的作用。

玩转RabbitMQ之三:理解消息通信中的概念_第2张图片
消息投递

       同样的,在消息投递中,RabbitMQ既不产生消息,也不处理消息,只是根据消息生产者设定的“地址”(绑定关系),将消息投递给相应的消费者“处理”(消费),RabbitMQ既不是消息的源点,也不是消息的终点,它其实是一个中转站、路由器。

二、生产者、消费者

       生产者(producer)产生消息,将其发到RabbitMQ代理服务器,消息上带有标签(简单地说就是投递地址),RabbitMQ根据标签将消息投递给相应的消费者(consumer)。这种通信方式是一种“发后即忘”(fire-and-forget)的单向方式,生产者虽然给消息设置了投递地址/规则,但是不一定知道最终消息会被哪个消费者消费,这跟邮件投递略有不同,比如生产者设置了路由键是aaa.bbb,那么消息可能被消费路由键为aaa.*或*.bbb的消费者消费,但生产者不关心消息最终去了哪里,发完就不管了,所以叫“发后即忘”。

玩转RabbitMQ之三:理解消息通信中的概念_第3张图片
"发后即忘"模式

        在玩转RabbitMQ之二:RabbitMQ简史中谈到了发布订阅(PubSub)模式,消费者向MQ订阅消息,当符合订阅规则的消息到达MQ时,MQ就会投递给其中一个消费者,但消费者不会知道是谁产生的消息,也不知道生产者消息的具体路由规则,消费者只是知道收到了符合它设定规则的消息的内容,生产者发布消息时包含了路由标签,但到达消费者时标签已被剥离。

        从下图,可以清楚看到生产者到消费者的消息流。

玩转RabbitMQ之三:理解消息通信中的概念_第4张图片
从生产者到消费者的消息流

三、信道

       应用程序要连接到RabbitMQ代理服务器,必须先建立一条TCP连接,由于TCP连接的建立代价很大,也比较消耗资源,如果所有的消息通信都使用TCP连接,计算机无法满足高并发的消息连接需求,为此,AMQP提出了一个信道的概念,一个AMQP连接(TCP)内可以创建多个信道,并且是无限制的,消息需要发布或消费时则动态建立信道跟MQ通信即可,这节省了宝贵的TCP连接资源,也提高了消息并发处理能力。

玩转RabbitMQ之三:理解消息通信中的概念_第5张图片
AMQP Channel

四、AMQP栈

        AMQP协议规定了路由消息必须有三个组成部分:交换器、绑定、队列,如下图所示:

玩转RabbitMQ之三:理解消息通信中的概念_第6张图片
AMQP栈

       生产者将消息发布到交换器,交换器根据绑定规则,将消息分发到对应的队列,消息出队后被消费者消费。

1、交换器和绑定

       交换器是一个虚拟的用来接收、投递消息的中间构件。生产者将消息发布到交换器上,并声明绑定的路由规则,然后交换器根据绑定路由规则,将消息投递到满足绑定路由规则的队列上。

       举个例子,你写信给女朋友Alice,信件上写明要投递到某个地址上面去,然后将信件投递到邮箱,接着邮递员从邮箱中取邮件,根据你填写的地址将信件发到Alice的家中,这里邮箱、快递员、信件上的地址就组成了交换器的效果,邮箱负责接收信件、快递员负责发信件、信件地址是投递信件的规则。

       当然,绑定规则跟信件地址并不是完全相等的关系,参考上面的“发后即忘”模型图。

       RabbitMQ中有三种常用的交换器,分别是direct、fanout、topic,还有一种headers交换器,允许匹配AMQP消息的header而非路由键,但其性能差因此几乎不会用到。

(1)direct交换器

        direct交换器是直接路由键匹配,如图所示:

玩转RabbitMQ之三:理解消息通信中的概念_第7张图片
direct交换器

        RabbitMQ默认实现了一个名字为空字符串的direct交换器,当声明一个队列时,默认会绑定到这个默认的交换器。

(2)fanout交换器

        fanout交换器会将收到的消息广播到绑定的队列上,比如应用程序中A模块完成之后必须触发B、C、D模块,并且B、C、D之前是并发的没有程序耦合关系,则适合对B、C、D分别声明一条队列来实现与A模块的关联,通过MQ,A模块无需显式地去调用BCD,实现了程序的解耦,这也是MQ存在的意义之一。

玩转RabbitMQ之三:理解消息通信中的概念_第8张图片
fanout交换器

(3)topic交换器

        topic交换器是RabbitMQ中最灵活的交换器,由于队列支持对绑定关系进行模糊匹配,因此能够实现多种复杂的应用场景需求,下面以日志搜集为例,实现对不同日志级别维度的日志进行收集,如图所示:

玩转RabbitMQ之三:理解消息通信中的概念_第9张图片
topic交换器

五、虚拟主机与隔离

        RabbitMQ默认的虚拟主机是: "/",vhost是AMQP的概念,每个vhost都有自己的队列、交换器和绑定,还有独立的权限机制,vhost存在的意义在于一个RabbitMQ服务器为多个应用程序提供队列服务时通过使用不同的vhost实现互相隔离,避免不同应用程序的队列服务互相干扰。

        RabbitMQ里创建用户时,通常会被指派至少一个vhost,并且只能访问被指派vhost内的队列、交换器和绑定。


六、RabbitMQ的持久化策略

1、持久化消息

       当RabbitMQ服务器发生宕机或者需要重启时,默认情况下消息、队列、交换器都会消失,为了避免消息、队列、交换器相关的数据丢失,需要将消息、交换器、队列都设置为持久化(durable属性)。

(1)消息持久化的原理

        对交换器和队列来说,当被设置为持久化时其相关数据会在磁盘中做备份,这样RabbitMQ服务器重启时会从磁盘中读取备份的数据恢复重建交换器和队列;对于消息来说,要实现持久化,除了将 “投递模式” (delivery mode)设为2之外,还需要将消息发布到持久化的交换器中并且到达持久化队列中才完成持久化存储,其他情况下的消息为无法应对服务器宕机和重启的非持久化。

        当消息经过持久化交换器到达持久化队列是,RabbitMQ会将消息写入磁盘上的持久化日志文件,当持久化队列中的持久化消息被消费时,RabbitMQ会将持久化日志中这条消息标记为等待垃圾回收,然后在某个时间点RabbitMQ将其从持久化日志中删除。

(2)消息持久化的缺点

        消耗性能,大幅度降低MQ服务器吞吐率;在MQ内建集群中工作得并不好,一旦某个集群节点宕机,这个节点上的队列不可用,直到节点恢复,而且出于系统容量和性能考虑,持久化消息不会在集群其他节点上备份,这导致未出队消息无法被消费,未写入到磁盘的消息也会丢失。

2、AMQP事务

        为了弥补消息丢失的风险,产生了AMQP事务,简单地说就是将消息的发布和提交到磁盘上视为一个整体事务(要么都成功,否则视为失败),如果你了解数据库中事务的概念,想必不难理解。

缺点

        大幅度降低系统消息吞吐量;由于消息发布和提交磁盘存储视为一个事务,会导致生产者应用程序发生同步。

3、发送方确认模式

        生产者发布消息时首先要将信道设置成confirm模式,当消息成功写入磁盘后,MQ会向生产者发送一条确认消息,生产者收到成功的确认消息才认定为消息发布成功,当生产者受到确认消息时就会触发回调处理确认消息。

优点

        既保持了消息发布和写入磁盘的事务一致性,又通过异步实现避免性能瓶颈。

你可能感兴趣的:(玩转RabbitMQ之三:理解消息通信中的概念)