集群部署方式

rabbitmq有三种方式来部署分布式集群系统。

  • Cluster
  • Federation
  • Shovel

开始学习

  • Cluster

我们平常本地开发用的一般是单机模式,这种仅此使用本地啦,生产环境一般都是Cluster集群。而Cluster集群一般也会分成两种,普通集群模式和镜像集群模式。

使用cluster集群,一般也有一些前提条件:

· 属于同一网段内的局域网节点,不支持跨网段
· rabbitmq、erlang版本需要相同
· 不同rabbitmq节点所使用的'.erlang.cookie'需要一致

此外还需要了解一些概念。

节点类型

· 磁盘节点(disc):将元数据存储在磁盘中,单节点系统只允许磁盘类型的节点,防止重启RabbitMQ的时候,丢失系统的配置信息。

· 内存节点(ram):内存节点将所有的队列、交换机、绑定、用户、权限和vhost的元数据定义存储在内存中,好处是可以使得像交换机和队列声明等操作更加的快速。

集群元数据

RabbitMQ 内部有各种基础构件,包括队列、交换器、绑定、虚拟主机等,这些构件以元数据的形式存在:

Queue元数据:队列的名称和声明队列时设置的属性(是否持久化、是否自动删除、队列所属的节点) 
Exchange元数据:交换机的名称、类型、属性(是否持久化等)  
Binding元数据:一张简单的表格展示了如何将消息路由到队列。包含的列有 Exchange名称、Exchange类型、routing\_key、queue\_name等  
vhost元数据:为vhost内队列、交换机和绑定提供命名空间和安全属性

一般启动rabbitmq时默认就为磁盘节点。

1、普通集群模式
rabbitmq01 disc
rabbitmq02 disc

在rabbitmq02上进行操作,执行命令加入集群

# 
# rabbitmqctl stop_app
Stopping rabbit application on node xnkl@rabbitmq02 ...
# rabbitmqctl join_cluster xnkl@rabbitmq01
Clustering node xnkl@rabbitmq02 with xnkl@rabbitmq01
# rabbitmqctl start_app
Starting node xnkl@rabbitmq02 ...
 completed with 3 plugins.
# 

观看rabbitmq02管控台,显示加入集群正常

image.png

接下在rabbitmq02上操作,添加exchange。可以看到添加交换机时,没有节点配置的选项,这是因为rabbitmq的集群,对于exchange的元数据在所有节点上都是一致的,并没有说是归属某个某个节点。

image.png

然后添加queue,可以看到当添加队列的时候,可以进行节点配置选择。意思是指明这个queue的所有者节点

image.png

然后在rabbitmq01的管控台上查看,在rabbitmq02上添加的exchange、queue都能同步到rabbitmq01上。

1.png

通过PHP脚本(PHP脚本连接的是rabbitmq01节点)发布消息,发现在rabbitmq01、rabbitmq02都能查看得到消息,然后在rabbitmq01设置消费者取出消息并ack,发现两个节点的队列‘queue02’都没有消息了。说明这个时候消息同步是正常的。

为作测试,发布5条消息进到队列‘queue02’,消息都为持久化消息。

image.png

继续测试异常情况。因为‘queue02’是属于节点rabbitmq02的,现在把rabbitmq02服务关闭,在rabbitmq02服务异常的情况下,可以从rabbitmq01的管控台看到‘queue02’队列的状态为‘down

image.png

无法正常查看队列的详情。

image.png

且通过管控台交换机列表,查看该队列的绑定关系,发现绑定暂时消失了。

image.png

此时再通过PHP脚本发布消息,发现不能正常投递(basic_publish),触发了'return_listener',以下为PHP脚本记录的日志内容。如果再声明该队列(queue_declare),会报错"NOT_FOUND - home node 'xnkl@rabbitmq02' of durable queue 'queue02' in vhost '/' is down or inaccessible"。如果设置消费者消费该队列(basic_consume),会报错NOT_FOUND - no previously declared queue

2020-04-23 17:15:03.579[ERROR]-[23]-[App\Utility\RabbitMQ\MQCommon.App\Utility\RabbitMQ\{closure}.(/www/mqs/App/Utility/RabbitMQ/MQCommon.php:323)]: MQS_MSG_PUBLISH_ERROR|消息投递到目标队列不成功|replyCode:312|replyText:NO_ROUTE|exchangeName:exchange02|routingKey:exchange02|logMark:|msg:createTime: 1587633303 usedCount: 0

当队列的所有者节点服务故障时,期间其他节点无法操作该队列(包括队列声明、队列消费、投递消息),也有一些异常情况出现(包括该队列状态为‘down’,有关该队列的绑定会暂时消失),只能等待该节点服务恢复正常,这是普通集群模式的缺点。

当rabbitmq02服务恢复正常时,再观察管控台面板情况,队列元数据、绑定元数据、队列消息都正常恢复。

image.png

2.png

·
·
·
·

上面测试的情况rabbitmq02是磁盘节点,现在换成内存节点再重新测试一遍看看。更换磁盘节点类型前,需要先把rabbitmq服务先停掉,否则会报错。执行以下命令:

# 
# rabbitmqctl change_cluster_node_type ram
Error: this command requires the 'rabbit' app to be stopped on the target node. Stop it with 'rabbitmqctl stop_app'.
Arguments given:
    change_cluster_node_type ram

Usage

rabbitmqctl [--node <node>] [--longnames] [--quiet] change_cluster_node_type <disc | ram>
# rabbitmqctl stop_app
Stopping rabbit application on node xnkl@rabbitmq02 ...
# rabbitmqctl change_cluster_node_type ram
Turning xnkl@rabbitmq02 into a ram node
# rabbitmqctl start_app
Starting node xnkl@rabbitmq02 ...
 completed with 3 plugins.
# 

可以看到更换成功。

image.png

但是更换节点类型之后,发现刚才的队列消失了(那队列里面原来的消失当然会随之消失),估计是更换节点类型会导致队列元数据清空的原因(猜测,没证实)。

image.png

但是exchange还是存在的,不过可以观察到绑定元数据也消失了。

image.png

image.png

此时创建队列、配置路由键,再进行上面的异常测试,把rabbitmq02节点服务停掉。发现出现的情况跟rabbitmq02为磁盘节点时一致(包括无法队列声明、队列消费、投递消息、该队列状态为‘down’、有关该队列的绑定会暂时消失)。

这可能跟在网上别的博客文章描述的有一点差异,就是如果队列的所有者节点(不论是内存节点还是磁盘节点)发生崩溃时,该节点上的队列元数据、绑定元数据不会丢失下图是截自网上别的文章,可以参考一下,因为可能存在版本不同而导致的不同结果。

image.png

所以,对于普通集群模式的队列,如果某个节点服务发生崩溃,那么归属该节点的队列都无法正常操作,只能等到该节点服务恢复。

2、镜像集群模式

镜像集群模式,是在普通集群模式的基础上实现高可用的。就是把交换机队列等做成镜像,存在于多个节点当中,属于rabbitmq的‘HA’策略方案。

在添加策略之前,队列‘queue02’里有5条消息。

image.png

先添加一个简单的策略,pattern=^是匹配所有的交换机和队列,priority优先级可以不设,ha-mode=all为针对集群内的所有节点。

image.png

当添加完上面的策略之后,查看队列的特点,可以看到应用的策略。但也发现一些红色异常的地方,意思应该是,rabbitmq01这个镜像还没有进行同步,因为在添加策略前,队列里已经存在一些消息。‘+1’的意思是当前集群只有2个节点,其中有一个节点还没进行同步。

image.png

这个时候,如果rabbitmq02的服务崩溃的话,其实也会有一些异常的情况。
· queue02队列的状态显示不正常,但它不像普通集群模式那样显示down。
· basic_publish,会发现不能正常发布消息,会触发return_listener
· queue_delcare,会报错"NOT_FOUND - failed to perform operation on queue 'queue02' in vhost '/' due to timeout"
· basic_consume,会报错"NOT_FOUND - no previously declared queue"
· 在管控台发现也是无法进行‘publish message’和‘get message’的。

image.png

image.png

这种情况显然是异常的,都无法正常操作队列。有一种解决办法就是手动执行同步镜像,"rabbitmqctl sync_queue queue02"。但是在同步队列镜像时,要确保rabbitmq02的服务是正常的,不然只会阻塞等待,然后超时报错。

image.png

执行完同步命令之后,管控台的‘红色’提醒消失。之后再继续发布消息也能够正常完成同步。此时,如果把rabbitm02服务停止的话,队列‘queue02’依然能够正常工作,正常发布消息,正常消费消息等。

这就是镜像集群模式比普通集群模式好的地方,实现高可用。消息实体会主动在镜像节点之间实现同步,而不是像普通模式那样,在consumer消费数据时临时读取。缺点就是,集群内部的同步通讯会占用大量的网络带宽。

image.png

还有另外一种办法,就是创建策略时,多设置一些参数,一般来说也不会单单设置一个‘ha-mode’而已,通常会跟这几个参数一起设置,如下。这样创建策略时,所有的队列镜像都能得到自动同步,不会再出现红色的异常提醒。

image.png

这样,rabbitmq的镜像集群算是配置好了。镜像集群也是生产应用比较普遍的模式,应该一般很少会用普通集群模式,毕竟短板明显摆在那里。不管当磁盘节点崩溃,还是队列的所有者节点崩溃,服务一样可以正常访问。

image.png

rabbitmq集群数据同步,因为考虑到存储空间、性能的原因,所以集群内部仅同步元数据。如三个节点组成了一个RabbitMQ的集群,Exchange A的元数据信息在所有节点上是一致的,而Queue(存放消息的队列)的完整数据则只会存在于它所创建的那个节点上,其他节点只知道这个queue的metadata信息和一个指向queue的owner node的指针。


  • Federation

待学习


  • Shovel

待学习


引用参考

https://blog.51cto.com/111346...
https://zhuanlan.zhihu.com/p/...
https://geewu.gitbooks.io/rab...
http://chyufly.github.io/blog...

写在最后

写的都是一些自己在开发过程遇到的问题,绝非官方确切解释,有不对的地方欢迎指正并讨论,互相学习互相进步,才是目的。

xnkl
9 声望1 粉丝

讨厌不写习惯注释和空格的程序猿,很