RabbitMQ 高可用

RabbitMQ 高可用集群搭建

前提条件:本文操作基于Linux Ubuntu系统

RabbitMQ 构建模式介绍

单一模式: 单机运行一个rabbitmq;

普通模式: 默认集群模式,以两个节点(rabbit01、rabbit02)为例进行说明。对于队列来说,消息实体只存在于其中一个节点rabbit01,rabbit01和rabbit02两个节点有相同的数据。当消息进入rabbit01节点的队列后,若消费者从2节点消费,则rabbitmq会临时在rabbit01、rabbit02间进行消息传输,把rabbit01中的消息实体取出并经过rabbit02发送给consumer。所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理queue。否则无论consumer连rabbit01还是rabbit02,出口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法取到rabbit01节点中还未消费的消息实体。如果做了消息持久化,那么得等rabbit01 节点恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象

镜像模式(本文的方案): 基于普通模式集群,在策略里面添加镜像策略。把需要的队列做成镜像队列,属于HA方案。该模式解决了普通模式中的问题,根本区别在于消息实体会主动在镜像节点间同步,而不是在客户端拉取数据时临时拉取。当然弊端就是降低系统性能,大量消息写入,集群内的网路带宽会被大大消耗。

高可用集群搭建:基于RabbitMQ镜像模式

镜像模式高可用集群搭建

如果RabbitMQ集群是多个Broker节点组成的,那么从服务的整体可用性上来说,该集群对于单点故障时有弹性的,但是同时也需要注意:尽管交换机和绑定关系能够在单点故障问题上幸免于难,但是队列和其上的存储的消息却不行,这是因为队列进程及其内容仅仅维持在单个节点之上,所以一个节点的失效表现为其对应的队列不可用

引入镜像队列的机制,可以将队列镜像到集群中的其他Broker节点之上,如果集群中的一个节点失效了,队列能自动地切换到镜像中的另一个节点上以保证服务的可用性。在通常的用法中,针对每一个配置镜像的队列都包含一个主节点(master)和若干个从节点(slave),相应的结构可以参考下图:

slave会准确地按照master执行命令的顺序进行动作,故slave与master上维护的状态应该是相同的。如果master由于某种原因失效,那么“资历最老”的slave会被提升为新的master。根据slave加入的时间排序,时间最长的slave即为“资历最老”。发送到镜像队列的所有消息会被同时发往master和所有的slave上,如果此时master挂掉了,消息还会在slave上,这样slave提升为master的时候消息也不会丢失。除发送消息(Basic.Publish)外的所有动作都只会向master发送,然后再由master将命令执行的结果广播给各个slave

如果消费者与slave建立连接并进行订阅消费,其实质上都是从master上获取消息,只不过看似是从slave上消费而已。比如消费者与slave建立了TCP连接之后执行一个Basic.Get的操作,那么首先是由slave将Basic.Get请求发往master,再由master准备好数据返回给slave,最后由slave投递给消费者。这里的master和slave是针对队列而言的,而队列可以均匀地散落在集群的各个Broker节点中以达到负载均衡的目的,因为真正的负载还是针对实际的物理机器而言,而不是内存中驻留的队列进程

RabbitMQ的镜像队列同时支持publisher confirm和事务两种机制。在事务机制中,只有当前事务在全部镜像中执行之后,客户端才会收到Tx.Commit-Ok的消息。同样的,在publisher confirm机制中,生产者进行当前消息确认的前提是该消息被全部镜像所接收了

RabbitMq Linux环境安装

不同linux操作系统,命令可能存在差异。
以下只提供了apt-get的安装方式

安装前准备

请查看高可用环境基础配置文档

Docker部署单机集群

该场景一般仅用于测试,生产环境请使用多机集群部署来实现

在部署服务器上面创建以下docker-compose.yml:

version: '3'
services:
    rabbit1:
      image: harbor.hipermatrix.com:10300/edge/rabbitmq-cluster:3.8.6-management
      hostname: rabbitmq1
      container_name: rabbitmq1
      environment:
        - ERLANG_COOKIE=.erlang.cookie.cluster
      ports:
        - "5672:5672"
        - "15672:15672"
      networks:
        - iot
    rabbit2:
      image: harbor.hipermatrix.com:10300/edge/rabbitmq-cluster:3.8.6-management
      hostname: rabbitmq2
      container_name: rabbitmq2
      environment:
        - ERLANG_COOKIE=.erlang.cookie.cluster
        - CLUSTER_WITH=rabbitmq1
        - ENABLE_RAM=true
        - RABBITMQ_LOGS=/var/log/rabbitmq/server.log
      ports:
        - "5673:5672"
        - "15673:15672"
      networks:
        - iot
    rabbit3:
      image: harbor.hipermatrix.com:10300/edge/rabbitmq-cluster:3.8.6-management
      hostname: rabbitmq3
      container_name: rabbitmq3
      environment:
        - ERLANG_COOKIE=.erlang.cookie.cluster
        - CLUSTER_WITH=rabbitmq1
        - ENABLE_RAM=true
        - RABBITMQ_LOGS=/var/log/rabbitmq/server.log
      ports:
        - "5674:5672"
        - "15674:15672"
      networks:
        - iot
networks:
  iot:
    external: true   

如果docker的iot网络不存在,请使用 docker network create iot 创建出来

然后执行以下命令启动rabbitmq集群

docker-compose up -d

大约等待30秒左右,集群启动完毕

Docker部署多节点集群

实际生产环境中,都是部署多节点集群,假设已经准备了3个以下的节点:

节点1: 192.168.1.101
节点2: 192.168.1.102
节点3: 192.168.1.103

节点1上执行:

docker run -d --name rabbitmq1 --hostname rabbitmq1 --net iot \
  --add-host rabbitmq2:192.168.1.102 --add-host rabbitmq3:192.168.1.103 \
  -e ERLANG_COOKIE=.erlang.cookie.cluster \
  -p 5672:5672 -p 15672:15672 -p 4369:4369 -p 25672:25672 -p 1883:1883 \
  -p 6000-6010:6000-6010 -p 35672-35682:35672-35682 \
  harbor.hipermatrix.com:10300/edge/rabbitmq-cluster:3.8.6-management

节点2上执行:

docker run -d --name rabbitmq2 --hostname rabbitmq2 --net iot \
  --add-host rabbitmq1:192.168.1.101 --add-host rabbitmq3:192.168.1.103 \
  -e ERLANG_COOKIE=.erlang.cookie.cluster -e CLUSTER_WITH=rabbitmq1 \
  -e RABBITMQ_LOGS=/var/log/rabbitmq/server.log -e ENABLE_RAM=true\
  -p 5672:5672 -p 15672:15672 -p 4369:4369 -p 25672:25672 -p 1883:1883 \
  -p 6000-6010:6000-6010 -p 35672-35682:35672-35682 \ 
  harbor.hipermatrix.com:10300/edge/rabbitmq-cluster:3.8.6-management

节点3上执行:

docker run -d --name rabbitmq3 --hostname rabbitmq3 --net iot \
  --add-host rabbitmq1:192.168.1.101 --add-host rabbitmq2:192.168.1.102 \
  -e ERLANG_COOKIE=.erlang.cookie.cluster -e CLUSTER_WITH=rabbitmq1 \
  -e RABBITMQ_LOGS=/var/log/rabbitmq/server.log -e ENABLE_RAM=true\
  -p 5672:5672 -p 15672:15672 -p 4369:4369 -p 25672:25672 -p 1883:1883 \
  -p 6000-6010:6000-6010 -p 35672-35682:35672-35682 \ 
  harbor.hipermatrix.com:10300/edge/rabbitmq-cluster:3.8.6-management

待3个节点的服务启动完成后,rabbitmq集群即搭建完成,访问任意一个节点的管理页面(如http://192.168.1.101:15672, 根据实际情况改变ip地址),在管理页面可以看到集群已经正常加入进来三个节点,如下图:

镜像模式高可用集群搭建(这里使用hanode3,hanode4来进行部署)

  1. 两台服务器搭建运行rabbitmq :
# 第一台服务器
apt-get install erlang-nox
apt-get install rabbitmq-server
rabbitmq-plugins enable rabbitmq_management
rabbitmq-plugins enable rabbitmq_web_stomp
# 第二台服务器
apt-get install erlang-nox
apt-get install rabbitmq-server
rabbitmq-plugins enable rabbitmq_web_stomp
rabbitmq-plugins enable rabbitmq_management
  1. 复制容器根目录下.erlang.cookie文件,应该是/var/lib/rabbitmq/.erlang.cookie,保证所有应用实例这个文件内容相同,可以复制,或者直接修改内容
scp /var/lib/rabbitmq/.erlang.cookie root@hanode4:/var/lib/rabbitmq/
  1. 重启第二个服务器并设置节点主从关系

第二台服务器加入集群

reboot
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@hanode3
rabbitmqctl start_app
  1. 主服务器添加管理员账号
rabbitmqctl add_user admin 123456
rabbitmqctl set_user_tags admin administrator
rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
  1. 设置镜像模式
    任意一个节点
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

查看状态:

rabbitmqctl cluster_status
  1. springboot项目使用方法
    配置yml中设置如下
spring:
  rabbitmq:
    addresses: 192.168.13.52:5672,192.168.13.53:5672
    username: admin
    password: 123456
  1. 使用公司脚手架,注入RabbitTemplate实例就可以直接操作。代码无需改动,需要注意的是写入的高可用。消费的方式还是监听,消费本身没有高可用一说。写入的时候会重试几次,然后调用成功,切换节点的时候可能会造成数据丢失。业务逻辑要考虑完整。
  2. 经过测试,发现这种方案并不能完全保证在服务突然下线导致的数据丢失问题。所以业务设计的时候要考虑突发情况数据丢失的可能性,并且如果业务不下线,使用的是某一个实例,所以有性能瓶颈问题。如果需要负载均衡,需要另外的实施手段,这里不做说明。
2022-08-11
0