您的位置:首页 > 其它

RabbitMQ知识盘点【贰】_实现原理及RabbitMQ集群

2018-01-08 16:23 239 查看
上一篇文章:《RabbitMQ知识盘点【壹】_消息队列介绍及三种消息路由模式

在上文中说明了RabbitMQ的各个组件和三种消息分发模式。本文首先尝试继续说下其实现原理。

Erlang语言

RabbitMQ是通过Erlang语言编写的,Erlang语言天生就能够让应用程序无须知道对方是否在同一个机器上即可互相通信,这让RabbitMQ集群和可靠的消息路由变得简单。

RabbitMQ中的每个队列、交换器和绑定的元数据(除了消息内容)都是保存在Mnesia的。Mnesia是内建在Erlang的NOSQL数据库。Mnesia通过将RabbitMQ元数据首先写入一个仅限追加的日志文件以确保其完整性。如果队列被声明为可持久化的(durable=true),那么它将被添加到rabbit_queue和rabbit_durable_queue两张表中;否则只添加到rabbit_queue表中。同理交换器则是rabbit_exchange和rabbit_durable_exchange。

RabbitMQ确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上一个持久化日志文件。当发布一条持久性消息到持久交换器上时,RabbitMQ会在消息提交到日志文件后才发送响应。消息持久化比较消耗性能,而且持久性消息在RabbitMQ内建集群环境下工作并不好。



当队列绑定到direct或者fanout交换器上时,RabbitMQ会在rabbit_route和rabbit_reverse_route表上创建条目来记录绑定信息;当绑定到topic交换器的时候,还需要额外保存rabbit_topic_trie_binding和rabbit_topic_trie_edge表。rabbit_topic_trie_binding表上保存的就是映射的规则,比如a.b.c.d这种模式,在rabbit_topic_trie_binding会创建四条记录。

至于交换器其实是个虚拟的概念,当把消息发布到交换器时,实际上是由所连接到的信道将消息上的路由键同交换器的绑定列表进行比较,然后路由消息。所以是信道(channel)按照绑定匹配的结果,将消息路由到队列。说白了,交换器就是一个路由表,一列是消息匹配规则,另一列是队列(相当于一个进程)的id。

那么如果消息已发送到生产者且在被路由之前,负责路由到的队列节点挂了,这样如何保证这条消息不丢失?

两种方法:

1.使用AMQP事务,在消息路由到队列之前它会一直堵塞;

2.使用发送方确认(publisher confirm)模式,记录连接中断时尚未却确认的消息;

RabbitMQ集群

在上文中我们写的测试用例只是单机模式的demo,在实际生产环境中,为了保证服务的高可用,我们一般会部署一系列RabbitMQ服务器形成集群提供服务。下面我们说说RabbitMQ集群。

同Redis集群一样,RabbitMQ集群对客户端来说是透明的。作为客户端,我不用关心我的缓存/消息被路由到了哪台服务器上,你只要保证我可以拿到我想要的缓存/消息就好。一般来说,客户端通过负载均衡连入RabbitMQ集群,相当于随机同集群内的一个节点建立连接。后续的所有操作其实都是基于客户端和这个节点的连接传输的,包括队列的创建等。注意,在集群模式下创建队列,只会在单个节点而不是所有节点上创建完整的队列信息(元数据、状态、内容)。只有队列的所有者节点知道有关队列的所有信息,其他非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针。比如客户端连接了A节点后,而想拉取B节点上队列中的消息时,RabbitMQ会临时在A、B节点之间进行消息传输,把B中的消息取出并经过A发送给客户端。

这里就有个问题,因为集群中各节点的队列是不会互相同步的,所以当一个节点崩溃时,那么该节点的队列和关联的绑定就消失了。附加在那些队列上的消费者丢失了其订阅信息,并且任何匹配改队列信息的新信息也都丢失了。当且仅当队列最开始没有被设置成可持久化时,消费者才可以重连到集群并重新创建队列。这样设计的目的是保持集群中队列的唯一性,当失败节点恢复后加入集群,该节点上的队列消息才不会丢失。

当一个节点停止服务后,客户端能够重新连接到集群中任意一个其他节点继续生产或消费消息。

在集群中创建队列,只在单个节点而不是所有节点上创建完整的队列信息(元数据、状态、内容)。只有队列的所有者节点知道有关队列的所有信息,其他非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针。因此当集群节点崩溃时,该节点的队列和关联的绑定就消失了。附加在那些队列上的消费者丢失了其订阅信息,并且任何匹配改队列信息的新信息也都丢失了。当且晋档队列最开始没有被设置成可持久化时,消费者彩可以重连到集群并重新创建队列。这样设计的目的是,当失败节点恢复后加入集群,该节点上的队列消息不会丢失。

而交换器并不存在这个问题,如上文所说交换器其实就是个配置表,因此在分布式消息队列下,交换器的复制很简单。



在单机模式下,所有的队列、交换器、绑定、用户、权限和vhost定义都必须保存在磁盘中,否则重启RabbitMQ后所有关于系统的配置信息都会丢失。不过在集群模式下,可以配置部分节点为内存节点,因为集群节点之间的元数据会同步。当在集群中声明队列、交换器或绑定的时候,这些操作会知道所有集群节点都成功提交元数据变更后才返回。RabbitMQ要求在集群中至少有一个磁盘节点。如果这一个磁盘节点挂了,那么集群还可以继续路由消息,但是不能对上述元数据进行创建、修改和删除操作了。

除了上述的普通集群方式,RabbitMQ还支持一种镜像集群的方式。镜像集群就是,可以配置集群中部分或全部节点都持有一份队列的信息。相当于消息会在配置持有拷贝的节点之间进行实时同步,这样实现会带来性能上的降低和集群内部的网络带宽将会被这种同步通讯大大消耗掉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: