一、集群结构
RabbitMQ会始终记录以下四种类型的内部元数据:
队列元数据:队列的名称、属性(可持久化,能否自动删除);
交换器元数据:交换器名称、类型和属性(可持久化等);
绑定元数据:一张记录了如何将消息路由到队列的表;
vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性;
在单一节点内,RabbitMQ会将这些信息存储在内存中,同时将那些标记为可持久化的队列和交换器(以及绑定)存储到硬盘上。
1.1、集群中的队列
默认情况下,当在集群中创建队列时,集群只会在单个节点上创建完整的队列信息(元数据、状态、内容),结果是只有队列的所有者节点知道有关队列的所有信息,所有其他非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针。因此当集群中某个节点崩溃时,该节点上的队列和关联的绑定都消失了。直到崩溃的节点恢复前,消费者都无法消费该队列上的消息了。
为什么默认情况下RabbitMQ不将队列内容和状态复制到所有节点上呢?
存储空间
性能。对于持久化消息来说,每一条消息都会触发磁盘I/O,每次新增节点时,网络和磁盘负载都会增加。
1.2、集群中的交换器
交换器实际上只是一个名称和一个队列绑定列表。当将消息发布到交换器时,实际上是由连接的信道将消息上的路由键跟交换器的绑定列表进行匹配,然后将消息路由到相应的队列。
由于交换器只不过是一张查询表,所以将交换器在整个集群中复制起来很简单,这样集群中的每个节点都拥有每个交换器的所有信息。
1.3、内存节点和磁盘节点
集群中的RabbitMQ节点,分为内存节点(RAM node)和磁盘节点(disk node)。内存节点将所有元数据、用户、权限等存储在内存中,磁盘节点将元数据存储在磁盘上。对于单节点系统,只有磁盘节点,否则重启RabbitMQ,所有关于系统的配置信息都会消失。
内存节点能保证集群的性能,磁盘节点保证集群配置信息不会因为重启而丢失。RabbitMQ要求在整个集群中最少有一个磁盘节点,其他节点都可以是内存节点。
1.4、镜像队列
默认情况下,队列只存在于集群中的一个节点上。RabbitMQ采用“镜像队列”选项,将队列拷贝到其他节点上,一旦主队列不可用,消费者可以连接到其他节点上的队列继续工作。
要使用镜像队列,需要声明一个Policy,该Policy可以匹配合适的队列作为镜像队列。
二、配置集群
2.1、准备环境
主机名 |
IP地址 |
操作系统 |
节点类型 |
master |
192.168.0.201 |
CentOS 7.7 |
磁盘节点 |
slave1 |
192.168.0.202 |
CentOS 7.7 |
内存节点 |
slave2 |
192.168.0.203 |
CentOS 7.8 |
内存节点 |
2.2、配置/etc/hosts文件(三个节点上都要操作)
[root@master ~]# cat /etc/hosts …… 192.168.0.201 master 192.168.0.202 slave1 192.168.0.203 slave2
2.3、安装RabbitMQ(三个节点上都要操作)
2.3.1、配置repo
[root@master ~]# cat /etc/yum.repos.d/erlang_solutions.repo [erlang-solutions] name=Centos $releasever - $basearch - Erlang Solutions baseurl=http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/erlang_solutions.asc enabled=1 [root@master ~]# cat /etc/yum.repos.d/rabbitmq.repo [rabbitmq_rabbitmq-server] name=rabbitmq_rabbitmq-server baseurl=https://packagecloud.io/rabbitmq/rabbitmq-server/el/7/$basearch repo_gpgcheck=1 gpgcheck=0 enabled=1 gpgkey=https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt metadata_expire=300 [root@master ~]# wget -O /etc/pki/rpm-gpg/erlang_solutions.asc https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc [root@master ~]# yum clean all [root@master ~]# yum makecache [root@master ~]# yum -y install erlang [root@master ~]# yum -y install rabbitmq-server
2.3.2、获取配置文件
从GitHub上获取模板文件:https://github.com/rabbitmq/rabbitmq-server/blob/master/docs/rabbitmq.conf.example
将配置文件命名为:/etc/rabbitmq/rabbitmq.conf
2.4、在master上启动RabbitMQ(在master上操作)
先添加Web管理插件(警告:在生产环境使用Web管理插件是不安全的):
[root@master ~]# rabbitmq-plugins list | grep management [root@master ~]# rabbitmq-plugins enable rabbitmq_management
启动RabbitMQ:
[root@master ~]# rabbitmq-server -detached
在master上查看端口:
[root@master ~]# netstat -tlnp tcp 0 0 0.0.0.0:4369 0.0.0.0:* LISTEN 34678/epmd tcp 0 0 0.0.0.0:15672 0.0.0.0:* LISTEN 41444/beam.smp tcp 0 0 0.0.0.0:25672 0.0.0.0:* LISTEN 41444/beam.smp tcp6 0 0 :::5672 :::* LISTEN 41444/beam.smp
会发现四个新的端口(4369、25672、5672、15672):
4369被empd进程使用,全称为Erlang Port Mapper Daemon。当启动一个分布式Erlang节点时,它会用empd进程进行注册,提供OS内核分配的地址和端口;
25672就是由OS分配给运行RabbitMQ的Erlang VM的;
5672则是AMQP客户端连接RabbitMQ的端口;
15672是Web管理插件启用的端口。
2.5、同步.erlang.cookie(在master上操作)
Erlang节点通过匹配Erlang cookie获得认证。当创建RabbitMQ集群时,需要保证各节点的Erlang cookie一致,才能让节点之间互相通信。cookie一般存放在“~/.erlang.cookie”文件中,“~”通常指rabbitmq用户的家目录。
[root@master ~]# scp /var/lib/rabbitmq/.erlang.cookie root@slave1:/var/lib/rabbitmq/ [root@master ~]# scp /var/lib/rabbitmq/.erlang.cookie root@slave2:/var/lib/rabbitmq/
2.6、将slave1和slave2加入集群,并作为内存节点启动(在slave1和slave2上操作)
[root@slave1 ~]# rabbitmq-plugins enable rabbitmq_management [root@slave1 ~]# rabbitmq-server -detached [root@slave1 ~]# rabbitmqctl stop_app [root@slave1 ~]# rabbitmqctl reset [root@slave1 ~]# rabbitmqctl join_cluster --ram rabbit@master [root@slave1 ~]# rabbitmqctl start_app
2.7、查看集群状态(任意节点都可执行)
现在来查看一下集群状态(图片只显示了部分内容):
2.8、改变节点类型
如果想将slave2改变为磁盘节点,可以进行以下操作:
[root@slave2 ~]# rabbitmqctl stop_app [root@slave2 ~]# rabbitmqctl change_cluster_node_type disc Turning rabbit@slave2 into a disc node [root@slave2 ~]# rabbitmqctl start_app
2.9、移除集群中的节点
如果想将slave2从集群中移除,进行以下操作:
[root@slave2 ~]# rabbitmqctl stop_app [root@slave2 ~]# rabbitmqctl reset [root@slave2 ~]# rabbitmqctl start_app
操作完记得把slave2再加回集群,方便进行以下的步骤。
三、镜像队列
创建一个RabbitMQ集群,就这样简单。前面提到(1.4节所述),在默认情况下,队列只存在于集群中的一个节点上。那如何保证当队列存在的节点崩溃时,消费者仍然可以消费该队列中的消息呢?这就是镜像队列的功能了。
关于镜像队列,移步到Web管理控制台上去看看。
3.1、创建一个超级权限用户
当启用Web管理插件时,有一个超级权限用户“guest”,密码为“guest”。但是“guest”用户默认只允许本地登录。
为了使用Web插件,临时创建一个有超级权限的用户(该用户将会同步到集群中的所有节点):
[root@master ~]# rabbitmqctl add_user admin admin [root@master ~]# rabbitmqctl set_user_tags admin administrator #为admin添加管理员权限 Setting tags for user "admin" to [administrator] ... [root@master ~]# rabbitmqctl set_permissions admin ".*" ".*" ".*" #授予admin在默认vhost“/”上的配置、写、读权限 Setting permissions for user "admin" in vhost "/" ... [root@master ~]# rabbitmqctl list_users Listing users ... user tags admin [administrator] guest [administrator]
通过浏览器访问管理控制台: http://192.168.0.201:15672/
3.2、创建Policy:
依次点击:顶部“Admin” -> 右侧“Policies” -> 中间“Add/update a policy”。
填写信息:
Name: queue_ha Pattern: ^q # 表示匹配所有以“q”开头的队列和交换器。使用“^.”将应用于所有队列和交换器; Apply to:Exchanges and queues # 将规则用于所有匹配的交换器和队列; Priority: 0 # 优先级; Definition: ha-mode = all # ha-mode有三个选项,all表示同步到所有节点,exactly和nodes需要配合ha-params选择同步的节点个数; ha-sync-mode = automatic # 节点重启后,镜像队列的同步策略。automatic为自动同步,manual需要在节点上手动执行rabbitmqctl sync_queue [Queue name]来完成;
Policy创建成功后,登录另外两个节点的Web管理控制台,都能看到该“Policy”。如下图:
3.3、创建队列
创建两个队列,一个以“q”开头的“q1”队列(因为之前创建的Policy:“queue_ha”匹配以“q”开头的队列),一个以其他字母开头的“tq2”队列。
依次点击:顶部“Queues” -> 中间“Add a new queue”。
3.3.1、创建“q1”队列
填写信息:
Type: Classic Name: q1 Durability: Durable # 声明队列为持久化队列。记住消息持久化规则:Persistent类型的消息到达持久化队列; Node: rabbit@master # 队列的主节点; Auto delete: No # 如果设置为“Yes”,当有一个消费者订阅到此队列时,将不允许其他消费者订阅;
将光标放在蓝色的“+2”处,显示已经同步到另外两个节点了,并且已经应用了“queue_ha”Policy,且队列为“idle”可用状态。如下图:
3.3.2、创建“tq2”队列
步骤与3.3.1相同。
3.4、往两个队列中发布消息
3.4.1、往“q1”队列中发布消息
依次点击:顶部“Queues” -> “All queues”中点击“Name”为“q1”的队列 -> 下拉页面在左侧找到“Publish message”:
填写信息:
Delivery mode: 2-Persistent # 声明该消息为可持久化的消息 Headers: q1_message = m1 # 可以设置为任意字符串 Propeties: message_id = 1 # 设置属性 Payload: “Hello RabbitMQ” # 消息的有效载荷,即消息的内容
点击“Publish message”,页面中央出现“Message published”提示,如下图:
3.4.2、往“tq2”队列中发布消息
依次点击:顶部“Queues” -> “All queues”中点击“Name”为“tq2”的队列 -> 下拉页面在左侧找到“Publish message”:
3.5、模拟故障:
现在,已经存在两个队列了。一个为镜像队列“q1”,并且同步到其余两个节点;一个普通队列“tq2”,只存在于master节点上。并且两个队列中都发布了消息,如下图:
3.5.1、模拟master节点故障(在master节点上执行):
[root@master ~]# rabbitmqctl shutdown Shutting down RabbitMQ node rabbit@master running at PID 14879 Waiting for PID 14879 to terminate RabbitMQ node rabbit@master running at PID 14879 successfully shut down
关掉master上的RabbitMQ之后,移步到slave1的Web管理控制台: http://192.168.0.202:15672/。使用admin用户(密码为admin)登录后,查看队列状态,如下图:
先来看看“q1”队列,“Node”处的蓝色标记由“+2”变成了“+1”,并且其他状态并未改变;
再来看看“tq2”队列,“State”变为“down”的状态,“Messages”的状态也由原来的数字变成了“NaN”。
此时点击“tq2”,弹出了“Not found”页面,而“q1”队列正常。如下图:
正如在1.1.1节所述,集群中普通队列的非所有者节点只保留了该队列的元数据,却无法消费该队列中的消息了。
3.5.2、模拟master节点恢复重启
在master节点上执行:
[root@master ~]# rabbitmq-server -detached
再到Web管理控制台上查看,“q1”处显示“镜像队列在master上未同步”,如下图:
因为在3.2节,创建Policy时指定了镜像队列在节点重启之后自动同步;“tq2”为持久化队列,并且队里中的一条持久化的消息,所以一段时间后,两个队列的状态都恢复到master节点关闭之前的状态了,如下图:
四、为Rabbit做负载均衡
到现在为止,已经有了三个RabbitMQ节点,并且已经将重要的队列设置为镜像队列。但是客户端不得不手工指定访问单个节点,并且当单个节点故障时,不得不手工尝试其他节点。是时候为Rabbit集群设置负载均衡了。
Rabbit一般采用HAProxy做负载均衡。
4.1、安装HAProxy
在master节点上操作:
[root@master ~]# yum -y install haproxy
4.2、配置HAProxy
[root@master ~]# cat /etc/haproxy/haproxy.cfg global log 127.0.0.1 local2 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode tcp log global option tcplog option dontlognull retries 3 timeout connect 10s timeout client 1m timeout server 1m maxconn 3000 listen rabbitmq_local_cluster 192.168.0.201:5670 #前端IP,供生产者和消费者使用 mode tcp balance roundrobin #负载均衡选项 server rabbit 192.168.0.201:5672 check inter 5000 rise 2 fall 3 #负载均衡中的集群节点配置 server rabbit 192.168.0.202:5672 check inter 5000 rise 2 fall 3 server rabbit 192.168.0.203:5672 check inter 5000 rise 2 fall 3 listen private_monitoring :8000 #数据统计页面 mode http option httplog stats enable stats uri /stats stats refresh 5s
4.4、启动HAProxy:
[root@master ~]# systemctl start haproxy
4.4、通过HAProxy数据统计页面查看状态
更多:
RabbitMQ学习之一:理解RabbitMQ
https://blog.51cto.com/13568014/2495857
RabbitMQ学习之二:管理单节点RabbitMQ
https://blog.51cto.com/13568014/2496390