RabbitMQ上手记录–part 4-节点集群(单机多节点)

时间:2021-03-20 06:42:00

  现在互联网应用动不动就说要HA,好像不搞个HA都不好意思说自己的应用能承载高并发,大用户量访问。RabbitMQ这个经典的消息组件,也必然逃不掉单点失效的尴尬局面。当然在RabbitMQ在被广泛应用于互联网之后,就对这个HA的需求做了实现,提供了集群供。这是RabbitMQ的内置功能,就跟普通集群的作用一样,就是在某个节点发生异常时,让生产者和消费者能保持通讯状态,同时通过提供更多的节点来增加系统的吞吐量。

RabbitMQ集群简介

RabbitMQ的集群实现是基于OTP分布式通讯框架实现的,该框架由Erlang语言实现。当一个节点失效的时候,客户端会自动连接到集群里的另一个节点,整个过程调用方不受影响,就好像没有断开一样。同时当集群承受加大的访问量的时候,可以通过添加更多的节点,线性的增加吞吐量和提高性能。但是RabbitMQ集群不能保证消息不丢失(这是另外一个话题了)。

RabbitMQ的集群仅仅是增加节点,但是节点之间的队列数据是不会同步的,也就是各个节点有自己独立的队列数据。这是RabbitMQ的默认设置。所以并不是说实现了集群,就可以保证数据的一致,这点需要明确。

RabbitMQ的集群架构简介

1.元数据(metadata)

元数据是RabbitMQ跟踪节点、提供链接信息的基本数据信息。

这里的元数据包含以下内容

队列元数据:队列名称和属性(比如是否持久队列或者是否自动删除队列)

Exchange元数据:Exchange名称,类型以及属性

绑定元数据(Binding):可以理解为一个路由表,就是如何通过Exchange找到消息队列,将消息投到队列。

Vhost元数据:命令空间、队列、Exchange的权限信息以及vhost内部的绑定信息。

有了这些metadata,RabbitMQ就能跟踪节点的所在服务器、节点之间的关系以及其他跟踪信息。metadata可以保存在内存或者磁盘中。保存在磁盘的话,那么在重启RabbitMQ节点之后就能重建队列和exchange。

集群节点中的队列

在上面介绍集群的时候,提到了一个关键的点,就是队列数据只存在于本地节点,不会同步到各个节点。在一个集群里面,创建一个队列时,只会在本地节点创建队列信息。这样只有本地节点知道队列的所有信息,所以一旦这个节点失效了,这个队列和对应的绑定信息也会消失。对于消费者来说,就失去了订阅信息,新生成的消息也无法投递,由于投递到了黑洞。

理论上看起来可以通过metadata去恢复队列信息,但是如果在其他非失效节点恢复队列信息,那么会出现404错误。这样做的目的是确保消息只存在于失效的队列中。我们唯一能做的就是恢复失效的节点。(以上问题只存在于支持持久的队列,如果队列不是持久的,可以直接恢复,反正没有数据需要处理)。

那么RabbitMQ为什么不把队列数据同步到各个节点呢?有以下2个原因:

1.存储空间问题

如果每个节点都包含了队列信息,会导致多出N倍的数据存储空间。

2 性能问题

对应支持持久的消息队列,保存到每个节点中需要很多IO写入和带宽,从而影响整个集群的性能。

集群中的Exchange

整个正好跟消息队列相反,Exchange的信息是分布式的,也就是各个节点都具有一份完整的Exchange信息。实际上Exchange就是一个路由表,所以很容易将路由信息复制到各个节点。

RabbitMQ上手记录–part 4-节点集群(单机多节点)(集群中的queue和Exchange结构图,来自《RabbitMQ In Action》

集群节点类型

最后一部分理论,讲完就进入啪啪的敲代码实际操作。

RabbitMQ的集群根据存储类型可以分成两种类型:内存节点和磁盘节点。这里存储的是metadata,内存节点保存在内存中,磁盘节点保存在磁盘中。

如果只有一个节点(或者说是单节点),只能是磁盘节点,否则RabbitMQ重启之后就啥也找不回了。

如果至少有一个磁盘节点,那么其他节点可以定义成内存节点。存储在内存节点的好处当然是为了提高访问速度。而磁盘节固然重要,也不是越多越好。因为每次变更metadata数据的时候,都需要将数据写入到磁盘节点,那么这个写入会拖慢集群的访问效率。

所以至少保持一个磁盘节点,那么一旦重启或者恢复,其他节点也能正常恢复metadata数据。

好了理论讲了半天,自己都觉得啰嗦,详细的内容还是看文档吧。

练习在本机搭建集群节点

这个集群不一定是要在多个服务器跑多个节点,一个服务器上也可以运行多个节点,可以理解为多个进程监听不同的端口号,并且节点的名称不同。我这里演示的是在一个服务器上运行多个节点。

仅仅是搭建集群节点命令很简单

1.首先确认安装好了RabbitMQ,然后停止默认启动的那个节点

sudo rabbitmqctl stop_app

sudo rabbitmqctl stop

2.然后依次执行以下三个命令,实际上是创建了三个节点,不同的端口号和节点名称

sudo RABBITMQ_NODENAME=rabbit RABBITMQ_NODE_PORT=5672 rabbitmq-server -detached
sudo RABBITMQ_NODENAME=rabbit_1 RABBITMQ_NODE_PORT=5673 rabbitmq-server -detached
sudo RABBITMQ_NODENAME=rabbit_2 RABBITMQ_NODE_PORT=5674 rabbitmq-server –detached

这里使用了两个环境变量,分别是RABBITMQ_NODENAME和RABBITMQ_NODE_PORT,也就是定义了节点名称和节点的监听端口号。

运行成功之后,就应该有三个节点在运行,可依次执行以下命令验证

sudo rabbitmqctl -n rabbit@bogon status

sudo rabbitmqctl -n rabbit_1@bogon status

sudo rabbitmqctl -n rabbit_2@bogon status

这里运行rabbitmqctl时指定了节点的名称-n,格式为:节点名@主机名, bogon是我的主机名,要根据实际情况修改。

3.形成集群

目前为止有三个节点,但是这三个节点是独立运行的,没有任何关联,下面来建立集群关系。

我们以第一个节点为metadata的提供节点,其他2个节点作为集群节点加入,也就是第2和第3个节点要得到第一个节点的metadata信息。

首先停止节点2

sudo rabbitmqctl -n rabbit_1@bogon stop_app

重置节点2的metadata

sudo rabbitmqctl -n rabbit_1@bogon reset

将节点2加入到节点1中

sudo rabbitmqctl -n rabbit_1@bogon join_cluster rabbit@bogon

(这里加入节点后,节点2将作为磁盘节点)

启动节点2

sudo rabbitmqctl -n rabbit_1@bogon start_app

查看节点2的集群状态

sudo rabbitmqctl -n rabbit_1@bogon cluster_status

输出如下信息

[{nodes,[{disc,[rabbit@bogon,rabbit_1@bogon]}]},
  {running_nodes,[rabbit@bogon,rabbit_1@bogon]},

可以看到目前有两个节点,都是磁盘类型的节点,并且都在运行

继续加入节点3,前两部停止和重置一样,只是改个节点名称

sudo rabbitmqctl -n rabbit_2@bogon stop_app

sudo rabbitmqctl -n rabbit_2@bogon reset

将节点3加入到节点1,但是设置节点3位内存节点,注意最后的--ram参数

sudo rabbitmqctl -n rabbit_2@bogon join_cluster rabbit@bogon –ram

启动节点3并查看集群状态

sudo rabbitmqctl -n rabbit_2@bogon start_app

sudo rabbitmqctl -n rabbit_2@bogon cluster_status

输出信息如下

[{nodes,[{disc,[rabbit_1@bogon,rabbit@bogon]},{ram,[rabbit_2@bogon]}]},
  {running_nodes,[rabbit@bogon,rabbit_1@bogon,rabbit_2@bogon]},

可以看到目前有三个节点,第三个节点是内存节点。

好了,到目前为此单机多节点就搭建完了,那有什么用呢?上述显示的running_nodes都是可以被连接的,最起码增加了连接数,也有个最起码的概念,接下来会整理如何将节点分布运行在不同的服务器。