• 【单一模式】即单机情况不做集群,就单独运行一个 rabbitmq 而已。

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

  • 【镜像模式】把需要的队列做成镜像队列,存在与多个节点属于 RabbitMQ 的 HA 方案。该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。



单一模式(附带延迟队列插件安装)

RabbitMQ 默认不带延迟队列插件,可以到官网去下载指定版本的插件,并手动安装到 RabbitMQ 环境中,在这里我使用的 RabbitMQ 版本是:rabbitmq:3.8-management
延迟队列插件版本是:rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez

docker-compose 脚本

version: "3"
services:
    rabbitmq:
        image: rabbitmq:3.8-management
        container_name: rabbitmq
        ports:
            - 15672:15672
            - 5672:5672
        restart: always
        privileged: true
        volumes:
            - /etc/localtime:/etc/localtime
            - ./rabbitmq:/var/lib/rabbitmq
        network_mode: rabbitmq
        environment:
            - TZ=Asia/Shanghai
            - LANG=en_US.UTF-8
            - RABBITMQ_DEFAULT_USER=admin
            - RABBITMQ_DEFAULT_PASS=xxxxx

启动容器

docker-compose -f docker-compose.yml up -d

然后我们将下载到的插件拷贝到 rabbitmq 容器的 / plugins 目录下

官方网站下载对应版本的插件

插件下载地址:https://www.rabbitmq.com/community-plugins.html
找到rabbitmq_delayed_message_exchange下载
https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/tag/3.8.9

docker cp rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez myrabbit:/plugins

进入容器内,启动我们刚刚添加的插件

docker exec -it myrabbit bash

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

查看插件是否有安装成功

#在容器内执行:
rabbitmq-plugins list
#在宿主机中执行这个:
docker exec myrabbit rabbitmq-plugins list
#能看到我们新添加的插件就说明成功了

查看管理页面是否成功

出现这个就是成功

Java测试代码

spring:
  rabbitmq:
    host: xxx # rabbitmq的连接地址
    port: 5672 # rabbitmq的连接端口号
package cc.sunni.rabbitmqdelayedmessagedemo.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DelayedMessageConfig {
    public final static String QUEUE_NAME = "delayed.live.queue";
    public final static String EXCHANGE_NAME = "delayed.live.exchange";
    /**
     * 声明队列
     */
    @Bean
    public Queue queue() {
        return new Queue(DelayedMessageConfig.QUEUE_NAME);
    }
    /**
     * 声明交换机
     */
    @Bean
    CustomExchange customExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        //参数二为类型:必须是x-delayed-message
        return new CustomExchange(DelayedMessageConfig.EXCHANGE_NAME, "x-delayed-message", true, false, args);
    }
    /**
     * 绑定队列到交换器
     */
    @Bean
    Binding binding(Queue queue, CustomExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(DelayedMessageConfig.QUEUE_NAME).noargs();
    }
}
package cc.sunni.rabbitmqdelayedmessagedemo.component;
import cc.sunni.rabbitmqdelayedmessagedemo.config.DelayedMessageConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class DelayedMessageSender {
    private final Logger log = LoggerFactory.getLogger(DelayedMessageSender.class);
    @Autowired
    private AmqpTemplate rabbitTemplate;
    public void send(String msg, Integer delaySeconds) {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.info("发送时间:" + sf.format(new Date()));
        rabbitTemplate.convertAndSend(DelayedMessageConfig.EXCHANGE_NAME, DelayedMessageConfig.QUEUE_NAME, msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                // 给消息设置延迟毫秒值
                message.getMessageProperties().setHeader("x-delay", delaySeconds * 1000);
                return message;
            }
        });
    }
}
package cc.sunni.rabbitmqdelayedmessagedemo.component;
import cc.sunni.rabbitmqdelayedmessagedemo.config.DelayedMessageConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
@RabbitListener(queues = DelayedMessageConfig.QUEUE_NAME)
public class DelayedMessageReceiver {
    public static final Logger log = LoggerFactory.getLogger(DelayedMessageReceiver.class);
    @RabbitHandler
    public void process(String msg) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.info("接收时间:" + sdf.format(new Date()));
        log.info("消息内容:" + msg);
    }
}
package cc.sunni.rabbitmqdelayedmessagedemo.controller;
import cc.sunni.rabbitmqdelayedmessagedemo.component.DelayedMessageSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MqController {
    public static final Logger log = LoggerFactory.getLogger(MqController.class);
    @Autowired
    private DelayedMessageSender sender;
    @PostMapping("/mq/{message}/{delay}")
    public String messageWithMQ(@PathVariable(value = "message") String message,
                                @PathVariable(value = "delay") Integer delay) {
        log.info("Send: " + message);
        sender.send(message, delay);
        return "success";
    }
}



优化搭建步骤

上面的步骤有些麻烦,当我们需要频繁的搭建 RabbitMQ 环境的时候就不方便了,所以我们可以优化一下,做一些准备工作,让后续的容器搭建变得便利。
目的
一键创建并启动 RabbitMQ 容器,并且附带延迟队列插件
思路
在 rabbitmq 原有镜像的基础上,制作新镜像,在容器启动之后自行启用插件
步骤

#创建镜像文件夹:
mkdir image
cd image
#编写Dockerfile文件:
FROM rabbitmq:3.8-management
COPY rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez /plugins
RUN rabbitmq-plugins enable --offline rabbitmq_delayed_message_exchange
上面的内容是依赖于rabbitmq:3.8-management镜像,并且将当前目录下的插件文件拷贝到/plugins中,然后执行插件启用命令
此时image文件夹的目录结构是这样的:
- image
    Dockerfile
    rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez
记得把插件准备好
制作镜像:下面命令的. 不能省略
docker build -t myrabbitmq:3.8-management .
查看镜像:
docker images

修改原来的 docker-compose 脚本,修改镜像为:

version: "3"
services:
    rabbitmq:   
        container_name: myrabbit
        ports:
            - 15672:15672
            - 5672:5672
        restart: always
        volumes:
            - /etc/localtime:/etc/localtime
            - /home/mycontainers/myrabbit/rabbitmq:/var/lib/rabbitmq
        network_mode: mynetwork
        environment:
            - RABBITMQ_DEFAULT_USER=admin
            - RABBITMQ_DEFAULT_PASS=123456
        image: myrabbitmq:3.8-management # 只改了这里,其他都是一样的

重新启动即可,由此,我们后面每次搭建新的 RabbitMQ 环境都不用再手动安装插件了。



普通集群模式

======

1、docker-ce 安装

[root@localhost ~]# vim /etc/yum.repos.d/docker-ce.repo
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=0
[root@localhost ~]# yum install docker-ce -y

2、docker-compose 安装

[root@localhost ~]# yum install -y python-pip
[root@localhost ~]# pip install -U docker-compose
[root@localhost ~]# docker-compose version

3、先创建对应的挂载目录和 shell 脚本

mkdir -p /data/rabbitmq
docker network create --driver bridge --subnet 172.18.0.0/24 --gateway 172.18.0.1 my-net
# 脚本如下
cat /data/rabbitmq/init_rabbitmq.sh 
#!/bin/bash
#reset first node
echo "Reset first rabbitmq node."
docker exec -it rabbitmq01 /bin/bash -c 'rabbitmqctl stop_app'
docker exec -it rabbitmq01 /bin/bash -c 'rabbitmqctl reset'
docker exec -it rabbitmq01 /bin/bash -c 'rabbitmqctl start_app'
# build cluster(参数--ram表示设置为内存节点,忽略该参数默认为磁盘节点)
echo "Starting to build rabbitmq cluster with two ram nodes."
docker exec -it rabbitmq02 /bin/bash -c 'rabbitmqctl stop_app'
docker exec -it rabbitmq02 /bin/bash -c 'rabbitmqctl reset'
docker exec -it rabbitmq02 /bin/bash -c 'rabbitmqctl join_cluster --ram rabbit@rabbitmq01' 
docker exec -it rabbitmq02 /bin/bash -c 'rabbitmqctl start_app'
docker exec rabbitmq03 /bin/bash -c 'rabbitmqctl stop_app'
docker exec rabbitmq03 /bin/bash -c 'rabbitmqctl reset'
docker exec rabbitmq03 /bin/bash -c 'rabbitmqctl join_cluster --ram rabbit@rabbitmq01'
docker exec rabbitmq03 /bin/bash -c 'rabbitmqctl start_app'
#check cluster status
echo "Check cluster status:"
docker exec rabbitmq01 /bin/bash -c 'rabbitmqctl cluster_status'
docker exec rabbitmq02 /bin/bash -c 'rabbitmqctl cluster_status'
docker exec rabbitmq03 /bin/bash -c 'rabbitmqctl cluster_status'

4、编写 yml 文件

环境变量官网地址

version: '3'
services:
  rabbitmq01:
    image: rabbitmq:3.10-management
    container_name: rabbitmq01
    ports:
      - "15673:15672"
      - "5673:5672"
    hostname: rabbitmq01
    restart: always
    privileged: true
#    build:
#      context: .
#      target: production
    environment:
      - TZ=Asia/Shanghai
      - LANG=en_US.UTF-8
      - RABBITMQ_DEFAULT_USER=zhe
      - RABBITMQ_DEFAULT_PASS=123.com
      # 集群通信(节点认证作用,集群部署需要同步该值,且值必须相同)
      - RABBITMQ_ERLANG_COOKIE=rabbitcookie
      # 群集中的节点名称必须唯一(在集群中各节点使用节点名称来识别和联系彼此)
      - RABBITMQ_NODENAME:rabbitmq01
      #如果系统使用完全限定的域名(FQDNs)作为主机名,则RabbitMQ节点和CLI工具必须配置为使用所谓的长节点名。对于服务器节点,这是通过将RABBITMQ_USE_LONGNAME环境变量设置为true来实现的。在不重置节点的情况下,无法将其从短名称切换到长名称。
    volumes:
      - /data/rabbitmq/rabbitmq01/:/var/lib/rabbitmq        # 防止log日志报failed to open log file
      - /data/rabbitmq/rabbitmq01/:/var/log/rabbitmq
    networks:
      my-net:
        ipv4_address: 172.18.1.10

  rabbitmq02:
    image: rabbitmq:3.10-management
    container_name: rabbitmq02
    ports:
      - "15674:15672"
      - "5674:5672"
    hostname: rabbitmq02
    restart: unless-stopped
    privileged: true
    environment:
      # 集群通信
      - TZ=Asia/Shanghai
      - LANG=en_US.UTF-8
      - RABBITMQ_ERLANG_COOKIE=rabbitcookie
      - RABBITMQ_DEFAULT_USER=zhe
      - RABBITMQ_DEFAULT_PASS=123.com
      # 群集中的节点名称必须唯一(在集群中各节点使用节点名称来识别和联系彼此)
      - RABBITMQ_NODENAME:rabbitmq02
      #如果系统使用完全限定的域名(FQDNs)作为主机名,则RabbitMQ节点和CLI工具必须配置为使用所谓的长节点名。对于服务器节点,这是通过将RABBITMQ_USE_LONGNAME环境变量设置为true来实现的。在不重置节点的情况下,无法将其从短名称切换到长名称。
      - RABBITMQ_CLUSTERED=true                   # 当前容器身份是从,会执行rabbitmqctl join_cluster命令加入到集群中去; 
      - RABBITMQ_CLUSTER_WITH=rabbit@rabbitmq01   # join的参数
      - RABBITMQ_RAM_NODE=true                    # ram是以内存方式加入,忽略该参数默认为磁盘节点。  
    volumes:
      - /data/rabbitmq/rabbitmq02/:/var/lib/rabbitmq
      - /data/rabbitmq/rabbitmq02/:/var/log/rabbitmq
 #     - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
      # 如果要重建集群的话必须把rabbitmq lib目录中的/mnesi目录删除掉
    depends_on:        # 从节点都依赖于主节点(防止关机导致容器集群失败)
      - "rabbitmq01"
    privileged: true
    networks:
      my-net:
        ipv4_address: 172.18.1.20

  rabbitmq03:
    image: rabbitmq:3.10-management
    container_name: rabbitmq03
    ports:
      - "15675:15672"
      - "5675:5672"
    hostname: rabbitmq03
    restart: unless-stopped
#    links:
#      - rabbitmq01
    environment:
      # 集群通信
      - TZ=Asia/Shanghai
      - LANG=en_US.UTF-8
      - RABBITMQ_ERLANG_COOKIE=rabbitcookie
      - RABBITMQ_DEFAULT_USER=zhe
      - RABBITMQ_DEFAULT_PASS=123.com
      # 群集中的节点名称必须唯一(在集群中各节点使用节点名称来识别和联系彼此)
      - RABBITMQ_NODENAME:rabbitmq03
      #如果系统使用完全限定的域名(FQDNs)作为主机名,则RabbitMQ节点和CLI工具必须配置为使用所谓的长节点名。对于服务器节点,这是通过将RABBITMQ_USE_LONGNAME环境变量设置为true来实现的。在不重置节点的情况下,无法将其从短名称切换到长名称。
      - RABBITMQ_CLUSTERED=true
      - RABBITMQ_CLUSTER_WITH=rabbit@rabbitmq01
      - RABBITMQ_RAM_NODE=true        
    volumes:
      - /data/rabbitmq/rabbitmq03/:/var/lib/rabbitmq
      - /data/rabbitmq/rabbitmq03/:/var/log/rabbitmq
#      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
    depends_on:
      - "rabbitmq01"
    privileged: true
    networks:
      my-net:
        ipv4_address: 172.18.1.30
networks:
  my-net:
    external: true

5、加入集群

[root@localhost rabbitmq]# chmod +x init_rabbitmq.sh
[root@localhost rabbitmq]# sh init_rabbitmq.sh					 #初始化镜像集群

到此为止,我们已经完成了 RabbitMQ 普通模式集群的建立,启动了 3 个节点,1个磁盘节点和2个内存节点。但是如果磁盘节点挂掉后,数据就丢失了。所以我们这个集群方案需要进一步改造为镜像模式集群。
主从集群的不足: 默认情况下队列只位于主节点上,尽管他们可以从所有节点看到和访问,也就是说整个集群与主节点共存亡。
因此当主节点宕机时,无法进行自动的故障转移。
集群重启顺序
集群重启的顺序是固定的,并且是相反的
启动顺序:磁盘节点 => 内存节点
关闭顺序:内存节点 => 磁盘节点
最后关闭必须是磁盘节点,否则容易造成集群启动失败、数据丢失等异常情况
因为这里使用的是 Docker-Compose 所以我们让从节点都依赖于主节点,只有主节点启动后,从节点才会启动,关闭是自动先关闭的是从节点,注意我们一定不能直接关机,要输入 docker-compose down 把容器关了再关机否则容器造成集群失败~

depends_on:
  - "rabbitmq1"

6、配置 nginx 反向代理到镜像集群通信

[root@localhost rabbitmq]# cd /data/nginx
[root@localhost nginx]# vim nginx.conf
user  nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;
	
	proxy_redirect          off;
	proxy_set_header        Host $host;
	proxy_set_header        X-Real-IP $remote_addr;
	proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
	client_max_body_size    10m;
	client_body_buffer_size   128k;
	proxy_connect_timeout   5s;
	proxy_send_timeout      5s;
	proxy_read_timeout      5s;
	proxy_buffer_size        4k;
	proxy_buffers           4 32k;
	proxy_busy_buffers_size  64k;
	proxy_temp_file_write_size 64k;
	#rabbitmq管理界面
	upstream rabbitManage {
		server 192.168.235.13:15673;
		server 192.168.235.13:15674;
		server 192.168.235.13:15675;
	}
	server {
        listen       15672;
        server_name  192.168.235.13; 
        location / {  
            proxy_pass   http://rabbitManage;
            index  index.html index.htm;  
        }  

    }
}
# rabbitmq通信
stream{
	upstream rabbitTcp{
        server 192.168.235.13:5673;
        server 192.168.235.13:5674;
	    server 192.168.235.13:5675;
    }

    server {
        listen 5672;
        proxy_pass rabbitTcp;
    }
}

[root@localhost nginx]# docker run -it -d --name nginxRabbitmq -v /data/nginx/nginx.conf:/etc/nginx/nginx.conf  --privileged --net=my-net nginx:1.20.2

7、部署 portainer 容器界面化管理

docker run -itd -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --name portainer docker.io/portainer/portainer

访问 rabbitmq 的管理页面
访问: http://IP:15672 默认账号密码: zhe:123.com

镜像模式集群改造

镜像集群就是在主从集群的基础上,添加相应策略,将主节点消息队列中的信息备份到其它节点上,主节点宕机时,对整个集群不产生影响,使集群可以高可用。
镜像队列是 Rabbit2.6.0 版本带来的一个新功能,允许内建双活冗余选项,与普通队列不同,镜像节点在集群中的其他节点拥有从队列拷贝,一旦主节点不可用,最老的从队列将被选举为新的主队列。
镜像队列
如果 RabbitMQ 集群中只有一个 Broker 节点,那么该节点的失效将导致整体服务的临时性不可用,并且也可能会导致消息的丢失。可以将所有消息都设置为持久化,并且对应队列的 durable 属性也设置为 true,但是这样仍然无法避免由于缓存导致的问题:因为消息在发送之后和被写入磁盘井执行刷盘动作之间存在
一个短暂却会产生问题的时间窗。通过 publisherconfirm 机制能够确保客户端知道哪些消息己经存入磁盘,
尽管如此,一般不希望遇到因单点故障导致的服务不可用。
引入镜像队列 (Mirror Queue) 的机制,可以将队列镜像到集群中的其他 Broker 节点之上,如果集群中的一个节点失效了,队列能自动地切换到镜像中的另一个节点上以保证服务的可用性。

添加策略(可以在创建队列之前添加,也可以创建队列之后添加)

参考官方文档: Policies Classic Queue Mirroring
策略模板([] 表示可选参数,<> 表示必穿参数)

rabbitmqctl set_policy [-p <vhost>] [--priority <priority>] [--apply-to <apply-to>] <name> <pattern> <definition>
#参数						说明
-p vhost			对指定的vhost进行设置
name				policy的名称
pattern				queue的匹配模式(正则表达式)
definition			镜像定义:包含三个部分ha-mode,ha-params,ha-sync-mode
					ha-mode:指明镜像队列的模式。  all:集群中所有节点进行镜像; exactly:在指定个数节点进行镜像,节点个数由ha-params指定;  nodes:在指定节点进行镜像,节点名称由ha-params指定
					ha-params: ha-mode模式需要用到的参数
					ha-sync-mode: 消息的同步方式(automatic,manual)
priority			policy的优先级,当有多个策略指定同一个队列时,优先级高的策略生效

添加策略实例

rabbitmqctl set_policy h**a-11 '^11' '{"ha-mode":"all","ha-sync-mode":"automatic"}'

说明:策略正则表达式为 "^" 表示匹配所有队列名称, ^11 :表示匹配hello开头的队列
以上策略的添加,删除,查看都可以在web UI 界面操作。

镜像队列的工作原理

在某种程度上你可以将镜像队列视为拥有一个隐藏的fanout交换器,它指示着信道将消息分发到从队列上。

设置镜像队列

设置镜像队列命令:rabbitmqctl set_policy 名称 匹配模式(正则) 镜像定义, 例如设置名称为 ha 的镜像队列,匹配所有名称是 amp 开头的队列都存储在 2 个节点上的命令如下:
docker exec -it rabbitmq01 bash

设置策略匹配所有名称是 amp 开头的队列都存储在 2 个节点上
rabbitmqctl set_policy -p / ha “^amp*” ‘{“ha-mode”:“exactly”,“ha-params”:2}’
或者
设置策略匹配所有名称的队列都进行高可用配置
rabbitmqctl set_policy -p / ha “^” ‘{“ha-mode”:“all”,“ha-sync-mode”:“automatic”}’

查询策略

rabbitmqctl list_policies -p / #查看vhost下的所有的策略(policies)
可以看出设置镜像队列,一共有四个参数,每个参数用空格分割。
参数一		策略名称,可以随便填,此外我们命名为ha(高可用)
参考二		-p / 设置哪个虚拟主机,可以使用rabbitmqctl list_policies -p / 	查看vhost下所有的策略policies
参数三		队列名称的匹配规则,使用正则表达式表示
参数四		为镜像队列的主体规则,是json字符串,分为三个属性:ha-mode | ha-params | ha-sync-mode,分别的解释如下:
✔ ha-mode 镜像模式,分类all/exactly/nodes,all存储在所有节点、exactly存储x个节点、节点的个数由ha-params指定;nodes指定存储的节点上名称,通过ha-params指定
✔ ha-params		作为参数,为ha-mode的补充;
✔ ha-sync-mode  镜像消息同步方式:automatic(自动),manually(手动)

--》至此rabbitmq集群算是搭建完毕。
rabbitmqctl list_policies		# 查看镜像队列
rabbitmqctl clear_policy		# 删除镜像队列

总结

阿里云 消息队列 MQ 消息队列(Message Queue,简称 MQ)是构建分布式互联网应用的基础设施,通过 MQ 实现的松耦合架构设计可以提高系统可用性以及可扩展性,是适用于现代应用的最佳设计方案。如有消息队列 RocketMQ 版 阿里巴巴官方指定消息产品,成熟、稳定、先进的技术体系打造金融级消息服务,感受双十一产品的完美体验。
腾讯云消息队列 TDMQ(Tencent Distributed Message Queue,简称 TDMQ)是一款基于 Apache 顶级开源项目 Pulsar 自研的金融级分布式消息中间件。其计算与存储分离的架构设计,使得它具备极好的云原生和 Serverless 特性,用户按量使用,无需关心底层资源。它拥有原生 Java 、 C++、Python、GO 等多种 API,同时支持 kafka 协议以及 HTTP 协议方式接入,可为分布式应用系统提供异步解耦和削峰填谷的能力,具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。TDMQ 目前已应用在腾讯计费绝大部分场景,包括支付主路径、实时对账、实时监控、大数据实时分析等方面。

其他问题

♥ 如出现该过期警告:RABBITMQ_ERLANG_COOKIE 对 env 变量的支持已被弃用,将在未来的版本中移除。Use the $HOME/.erlang.cookie file or the --erlang-cookie switch instead.

root@rabbitmq02:/# rabbitmqctl join_cluster --ram rabbit@rabbitmq01
RABBITMQ_ERLANG_COOKIE env variable support is deprecated and will be REMOVED in a future version. Use the $HOME/.erlang.cookie file or the --erlang-cookie switch instead.
Clustering node rabbit@rabbitmq02 with rabbit@rabbitmq01
Error:
incompatible_feature_flags
原因:镜像版本不一致导致

♥ 配置相同 Erlang Cookie
有些特殊的情况,比如已经运行了一段时间的几个单个物理机,我们在之前没有设置过相同的 Erlang Cookie 值,现在我们要把单个的物理机部署成集群,实现我们需要同步 Erlang 的 Cookie 值。
1. 为什么要配置相同的 erlang cookie?
因为 RabbitMQ 是用 Erlang 实现的,Erlang Cookie 相当于不同节点之间相互通讯的秘钥,Erlang 节点通过交换Erlang Cookie获得认证。
2.Erlang Cookie 的位置
要想知道 Erlang Cookie 位置,首先要取得 RabbitMQ 启动日志里面的 home dir 路径作为根路径。使用:“docker logs 容器名称” 查看。
所以 Erlang Cookie 的全部路径就是 “/var/lib/rabbitmq/.erlang.cookie”。
注意:每个人的 erlang cookie 位置可能不同,一定要查看自己的 home dir 路径。
3. 复制 Erlang Cookie 到其他 RabbitMQ 节点
获取到第一个 RabbitMQ 的 Erlang Cookie 之后,只需要把这个文件复制到其他 RabbitMQ 节点即可。
物理机和容器之间复制命令如下:
▷ 容器复制文件到物理机:docker cp 容器名称: 容器目录 物理机目录
▷ 物理机复制文件到容器:docker cp 物理机目录 容器名称: 容器目录
设置 Erlang Cookie 文件权限:“chmod 600 /var/lib/rabbitmq/.erlang.cookie”。
Don’t give up your dreams because there is no applause.