单一模式定制app开发即单机情况不做集群,定制app开发就单独运行一个rabbitmq而已。
普通模式默认模式,定制app开发以两个节点(rabbit01、rabbit02)定制app开发为例来进行说明。对于Queue来说,定制app开发消息实体只存在于其中一个节点rabbit01(或者rabbit02),rabbit01和rabbit02定制app开发两个节点仅有相同的元数据,定制app开发即队列的结构。定制app开发当消息进入rabbit01节点的Queue后,consumer从rabbit02定制app开发节点消费时,RabbitMQ会临时在rabbit01、rabbit02定制app开发间进行消息传输,把A定制app开发中的消息实体取出并经过B发送给consumer。所以consumer定制app开发应尽量连接每一个节点,定制app开发从中取消息。定制app开发即对于同一个逻辑队列,定制app开发要在多个节点建立物理Queue。否则无论consumer连rabbit01或rabbit02,出口总在rabbit01,定制app开发会产生瓶颈。当rabbit01定制app开发节点故障后,rabbit02定制app开发节点无法取到rabbit01定制app开发节点中还未消费的消息实体。定制app开发如果做了消息持久化,那么得等rabbit01节点恢复,定制app开发然后才可被消费;定制app开发如果没有持久化的话,就会产生消息丢失的现象。
镜像模式把需要的队列做成镜像队列,存在与多个节点属于RabbitMQ的HA方案。该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。
普通集群模式
1、-ce安装
[root@localhost ~]# vim /etc/yum.repos.d/docker-ce.repo[docker-ce-stable]name=Docker CE Stable - $basearchbaseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stableenabled=1gpgcheck=0[root@localhost ~]# yum install docker-ce -y
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2、docker-compose安装
[root@localhost ~]# yum install -y python-pip[root@localhost ~]# pip install -U docker-compose[root@localhost ~]# docker-compose version
- 1
- 2
- 3
3、先创建对应的挂载目录和shell脚本
mkdir -p /data/rabbitmqdocker 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 nodeecho "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 statusecho "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'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
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.30networks: my-net: external: true
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
5、加入集群
[root@localhost rabbitmq]# chmod +x init_rabbitmq.sh[root@localhost rabbitmq]# sh init_rabbitmq.sh #初始化镜像集群
- 1
- 2
到此为止,我们已经完成了普通模式集群的建立,启动了3个节点,1个磁盘节点和2个内存节点
。但是如果磁盘节点挂掉后,数据就丢失了。所以我们这个集群方案需要进一步改造为镜像模式集群。
▷ 主从集群的不足: 默认情况下队列只位于主节点上,尽管他们可以从所有节点看到和访问,也就是说整个集群与主节点共存亡。
因此当主节点宕机时,无法进行自动的故障转移。
▷ 集群重启顺序
集群重启的顺序是固定的,并且是相反的
启动顺序:磁盘节点 => 节点
关闭顺序:内存节点 => 磁盘节点
最后关闭必须是磁盘节点,否则容易造成集群启动失败、数据丢失等异常情况
因为这里使用的是Docker-Compose所以我们让从节点都依赖于主节点,只有主节点启动后,从节点才会启动,关闭是自动先关闭的是从节点,注意我们一定不能直接关机,要输入docker-compose down把容器关了再关机否则容器造成集群失败~
depends_on: - "rabbitmq1"
- 1
- 2
6、配置nginx反向代理到镜像集群通信
[root@localhost rabbitmq]# cd /data/nginx[root@localhost nginx]# vim nginx.confuser 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
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
- 1
访问rabbitmq的管理页面
访问: http://IP:15672 默认账号密码:zhe:123.com
镜像模式集群改造
镜像集群就是在主从集群的基础上,添加相应策略,将主节点消息队列中的信息备份到其它节点上,主节点宕机时,对整个集群不产生影响,使集群可以高可用。
镜像队列是Rabbit2.6.0版本带来的一个新功能,允许内建双活冗余选项,与普通队列不同,镜像节点在集群中的其他节点拥有从队列拷贝,一旦主节点不可用,最老的从队列将被选举为新的主队列。
镜像队列
如果 RabbitMQ 集群中只有一个 Broker 节点,那么该节点的失效将导致整体服务的临时性不可用,并且也可能会导致消息的丢失。可以将所有消息都设置为持久化,并且对应队列的durable属性也设置为true,但是这样仍然无法避免由于缓存导致的问题:因为消息在发送之后和被写入磁盘井执行刷盘动作之间存在
一个短暂却会产生问题的时间窗。通过 publisherconfirm 机制能够确保客户端知道哪些消息己经存入磁盘,
尽管如此,一般不希望遇到因单点故障导致的服务不可用。
引入镜像队列(Mirror Queue)的机制,可以将队列镜像到集群中的其他 Broker 节点之上,如果集群中的一个节点失效了,队列能自动地切换到镜像中的另一个节点上以保证服务的可用性。
添加策略(可以在创建队列之前添加,也可以创建队列之后添加)
参考官方文档:
▷ 策略模板([]表示可选参数,<>表示必穿参数)
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的优先级,当有多个策略指定同一个队列时,优先级高的策略生效
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
▷ 添加策略实例
rabbitmqctl set_policy h**a-11 '^11' '{"ha-mode":"all","ha-sync-mode":"automatic"}'说明:策略正则表达式为 "^" 表示匹配所有队列名称, ^11 :表示匹配hello开头的队列以上策略的添加,删除,查看都可以在web UI 界面操作。
- 1
- 2
- 3
- 4
镜像队列的工作原理
在某种程度上你可以将镜像队列视为拥有一个隐藏的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 # 删除镜像队列
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
总结
阿里云
消息队列 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@rabbitmq01RABBITMQ_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@rabbitmq01Error:incompatible_feature_flags原因:镜像版本不一致导致
- 1
- 2
- 3
- 4
- 5
- 6
♥ 配置相同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.