rabbitmq集群搭建

为了确保高可用性和容错性,RabbitMQ 集群通常至少需要3个节点。使用至少3个节点的集群可以确保在一个节点出现故障时,集群仍然可以继续正常运行。通过在多个节点之间分配数据和负载,RabbitMQ 集群可以提供更高的可用性和性能。

在一个仅有两个节点的集群中,如果一个节点发生故障,集群将无法继续正常工作。因此,为了避免单点故障,至少需要3个节点。此外,选择奇数个节点有利于投票决策,避免在节点故障时出现平局的情况

官方说明的形成rabbitmq集群主要有以下方法:

  • 通过在配置文件中列出集群节点以声明方式
  • 以声明方式基于DNS的发现
  • 以声明方式使用AWS(EC2)实例发现
  • 声明式使用kubernetes发现
  • 以声明方式基于etcd的发现(通过插件)
  • 手动使用rabbitmqctl

集群部署

本例子演示通过三台机器上的docker容器来运行rabbitmq,如下:

宿主IP容器内主机名rabbitmq节点名称(计算生成)
192.168.49.224rabbitmq1rabbit@rabbitmq1
192.168.49.186rabbitmq2rabbit@rabbitmq2
192.168.49.83rabbitmq3rabbit@rabbitmq3

1、在每台机器上创建目录/data/rabbitmq,并新建hosts文件,内容如下:

127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters

192.168.49.224 rabbitmq1
192.168.49.186 rabbitmq2
192.168.49.83  rabbitmq3

注:这个hosts文件其实是先运行容器后,从里面把/etc/hosts文件拷贝出来,添加了rabbitmq的host后又挂载进去的,如果不这样做,直接修改容器里的/etc/hosts,一旦重启了容器,/etc/hosts配置内容会消失

2、在49.224机器上创建rabbitmq1容器,命令如下:

docker run -itd --name rabbitmq1 \
--hostname rabbitmq1 -p 4369:4369 \
-p 15672:15672 -p 5672:5672 \
-p 25672:25672 \
-v /data/rabbitmq/hosts:/etc/hosts \
-v /etc/localtime:/etc/localtime rabbitmq

3、在49.186机器上创建rabbitmq2容器,命令如下:

docker run -itd --name rabbitmq2 \
--hostname rabbitmq2 -p 4369:4369 \
-p 15672:15672 -p 5672:5672 \
-p 25672:25672 \
-v /data/rabbitmq/hosts:/etc/hosts \
-v /etc/localtime:/etc/localtime rabbitmq

4、在49.83机器上创建rabbitmq3容器,命令如下:

docker run -itd --name rabbitmq3 \
--hostname rabbitmq3 -p 4369:4369 \
-p 15672:15672 -p 5672:5672 \
-p 25672:25672 \
-v /data/rabbitmq/hosts:/etc/hosts \
-v /etc/localtime:/etc/localtime rabbitmq

5、将节点1容器中的.erlang.cookie拷贝到节点2和节点3的容器中,路径为:

docker cp rabbitmq1:/var/lib/rabbitmq/.erlang.cookie .

docker cp .erlang.cookie rabbitmq2:/var/lib/rabbitmq

docker cp .erlang.cookie rabbitmq3:/var/lib/rabbitmq

注:容器中的.erlang.cookie的属主和属组都要是rabbitmq才行,并且此文件的权限要设置为400,否则容器启动失败

6、接下来将rabbitmq2和rabbitmq3加入到rabbitmq1中,在rabbitmq2和rabbitmq3容器中执行如下命令:

rabbitmqctl stop_app                       #关闭RabbitMQ服务
rabbitmqctl reset                          #重置节点
rabbitmqctl join_cluster rabbit@rabbitmq1  #加入集群,rabbit@rabbitmq1为节点名
rabbitmqctl start_app                      #启动服务

添加后,通过任意节点的管理端口15672访问,输入账号密码guest/guest,即可看到节点信息,如图:

如果rabbitmq的15672不通,说明管理插件没启动,可进入容器中执行命令启动,如下:

rabbitmq-plugins enable rabbitmq_management

命令行查看集群状态:

注意几点:

  • rabbitmqctl stop 会将Erlang虚拟机关闭,容器会挂掉
  • rabbitmqctl stop_app 只关闭rabbitMQ服务
  • rabbitmqctl start_app 只启动应用服务
  • 节点必须重置后才能加入集群,重置节点会删除该节点上以前存在的所有资源和数据,保证空数据

集群自动发现机制:

如果集群中默认配置中启用了RabbitMQ的自动集群发现机制。那么在一个节点上创建Exchange或Queue时,它会自动地在整个集群中被发现,通过广播的机制,通过如下命令查看是否开启发现机制,如图:

在rabbitmq1创建一个队列,在rabbitmq2和rabbitmq3上也是可以看到的,如图:

发送消息

Exchanges(交换器)和 Queues(队列)是两个核心组件,它们共同工作以实现消息的发送和接收

Queues(队列):

  • 定义:队列是消息的存储位置,它们保持消息直到它们被消费者消费。队列可以被认为是一个消息的缓冲区
  • FIFO:队列通常按照先进先出(FIFO)的顺序处理消息,尽管某些特性(如优先级队列)可能会影响这一点
  • 持久性:队列可以被声明为持久化的,这意味着即使 RabbitMQ 服务器重启,队列中的消息也不会丢失
  • 独占性:队列也可以是独占的,这意味着它们仅与创建它们的连接相关联,并且在连接关闭时会被删除

Exchanges(交换器):

  • 定义:交换器充当消息的路由中心。生产者将消息发送到交换器,而不是直接发送到队列
  • 路由逻辑交换器根据类型和路由键将消息路由到一个或多个队列。交换器有几种类型,每种类型都有不同的路由逻辑:
  • direct:根据路由键将消息直接路由到具有匹配键的队列
  • fanout:将消息广播到所有绑定到该交换器的队列,忽略路由键
  • topic:根据路由键模式匹配将消息路由到队列
  • headers:根据消息的 headers 属性将消息路由到队列,忽略路由键
  • 持久性交换器也可以被声明为持久化的,这意味着服务器重启后交换器仍然存在

交换器和队列的工作流程:

  • 声明队列:消费者或生产者首先声明一个队列,可以选择设置其属性,如持久性、独占性等
  • 声明交换器:声明一个交换器,并设置其类型和持久性等属性
  • 绑定队列到交换器:将队列绑定到交换器上,可以选择一个路由键,这定义了交换器如何将消息路由到该队列
  • 发送消息:生产者发送消息到交换器,可以指定一个路由键
  • 路由消息:交换器根据其类型和路由键将消息路由到一个或多个绑定的队列
  • 消息消费:消费者从队列中消费消息,通常通过拉取(pull-based)或推模式(push-based)

下面演示下简单的发送消息

1、首先登录管理页面,创建队列(Queues),持久化方案选择持久(Durable),如图:

队列创建在了集群中的rabbitmq1节点上,如图:

2、创建交换器(Exchanges),类型选择路由键,如图:

3、将交换器绑定到队列,点击刚才创建的交换器,点击Bindings,如图:

点击Bind后即可绑定成功,此时如果切换到Queues页面,点击刚才创建的队列,也可以看到绑定过来的交换器,如图:

4、验证消息发送,点击创建的交换器new_chanage,下方有个Publish message,输入路由键,根据此路由键就能向绑定的队列发送消息,模式选择可持续,这样就能存在本地磁盘了,如图:

点击推送消息后,在队列页面可以看到Ready和Total已经显示有一条消息,如图:

登录rabbitmq1节点,在mnesia/rabbit@rabbitmq1/msg_stores/vhosts路径下可以看到创建的队列以及队列中消息,如图:

此时登录其他两个节点,可以发现并没有消息内容,因为虽然当前rabbitmq是集群,但是数据并没有做高可用同步,单个节点生产的消息只保留在了当前节点,没有同步到其他节点,此时如果这个节点挂了,数据就会消失,因此数据的高可用非常重要,看下面教程配置集群数据高可用

配置集群高可用

集群发现机制只是集群节点之前互相识别对方并通信,交换路由信息,以及协调队列和交换器等资源的分布,并不涉及数据复制,因此如果集群没有开启高可用策略,那么数据实际还是在当前节点存储,并没有同步到其他节点上,下面说下如何配置集群数据的高可用策略

1、登录任意节点,点击Admin–policies,如图:

设置策略名称,匹配模式,以及匹配范围,同步模式,如图:

  • pattern:匹配模式,^表示匹配所有
  • ha-mode:设置镜像队列为all,此模式下,队列会在集群中的所有节点上进行镜像
  • ha-sync-mode:同步模式,automatic表示自动同步,manual表示手动

在管理界面可以看到已经配置的策略,如图:

在命令行也可以看到创建的策略,如图:

设置完成后,观察其余两个节点,能看到mnesia目录下已经同步过来新的消息了

新增节点:按照上面的教程配置新节点,将新节点添加到集群后,数据也会自动同步到新节点

删除节点:删除节点很简单,首先停止要删除的节点上的rabbitmq,然后在其他节点执行如下命令:

rabbitmqctl forget_cluster_node rabbit@rabbitmq2  #删除节点2

附加

如果通过NGINX代理rabbitmq,那么配置文件可以这样写:

    location /rabbitmq/ {
          port_in_redirect on;
          proxy_redirect off;
          proxy_pass http://xxx.xxx.xxx:15672/;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header User-Agent $http_user_agent;
          proxy_set_header Host $http_host;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
    }
    location ~* /rabbitmq/api/ {
          rewrite ^ $request_uri;
          rewrite ^/rabbitmq/api/(.*) /api/$1 break;
          return 400;
          proxy_pass http://xxx.xxx.xxx:15672$uri;
          proxy_buffering                    off;
          proxy_set_header Host              $http_host;
          proxy_set_header X-Real-IP         $remote_addr;
          proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
    }

这样配置后,在调用API设置权限的时候就不会出现Management API returned status code 405, 也可以避免出现The object you clicked on was not found; it may have been deleted on the server错误

rabbitmq队列导出导入

rabbitmq在使用过程中,有时会涉及到迁移,此时就需要将队列导入到新的mq中,同时把消息也更新到最新的mq中

1、导出队列(Queues)

首先登陆源mq,进入到主页中的Overview页面,如图:

在主页面中找到,Export definitions,然后点击右侧的Download broker definitions,如图:

2、导入队列:

登陆目标mq中,也在主页面中,找到Import definitions,点击Upload broker definitions,如图:

rabbitmq消息数据备份与恢复

1、登陆到源服务器,执行rabbitmqctl命令,查看数据存储目录,如下:

rabbitmqctl eval 'rabbit_mnesia:dir().'
#/var/lib/rabbitmq/mnesia/rabbit@rabbitmq  #本例子数据地址

2、关闭源rabbitmq,防止数据不一致,如果容器运行的,直接执行如下:

docker stop rabbitmq 

3、备份rabbitmq消息数据,直接使用tar命令打包即可,如下:

tar -zcvf rabbitmq.tgz /var/lib/rabbitmq/mnesia/rabbit@rabbitmq

4、登陆到目标rabbitmq服务器,,然后将备份的数据解压到对应的目录即可,如下:

tar xf rabbitmq.tgz  /var/lib/rabbitmq/mnesia

注意:如果是容器方式运行的rabbitmq,要注意是否将mnesia目录挂在到外面来

4、修改数据目录权限(如果需要),命令如下:

chown rabbitmq:rabbitmq rabbit@rabbitmq

5、启动目标服务器的rabbitmq,如下:

docker start rabbitmq   或 systemctl start rabbitmq

常见命令

#查看队列数据但不删除
rabbitmqadmin -u username -p passwd get queue=queue-name  ackmode=ack_requeue_true count=10
#查看队列数据并删除掉,区别在于ack_requeue_false还是true
rabbitmqadmin -u username -p passwd get queue=queue-name  ackmode=ack_requeue_false count=10
#列出队列信息
rabbitmqadmin -u username -p passwd list queues name messages messages_ready messages_unacknowledged

rabbitmq问题

rabbitmq页面提示错误:

Network partition detected
Mnesia reports that this RabbitMQ cluster has experienced a network partition. There is a risk of losing data. Please read RabbitMQ documentation about network partitions and the possible s

表明 RabbitMQ 集群发生了 ​​网络分区(Network Partition)​​,即集群中的节点之间失去了网络连接,导致数据可能不一致或丢失

解决方案:在rabbitmq.conf中i添加配置:

cluster_partition_handling = pause_minority

配置pause_minority后,当发生网络分区时,RabbitMQ 会自动检测自身是否处于少数派(小于或等于集群中一半的节点数)。如果是少数派,则会自动暂停这些节点,直到网络分区结束,此配置可以保证数据一致性,防止脑裂现象,缺点是可能会导致部分节点不可用,需要网络恢复后手动或自动重启

标签