优化RabbitMQ集群
什么是真正的高可用
◆ 在传统以物理机/虚拟机为基础的架构中,服务宕机往往需要人工处理
◆ 随着容器技术的发展,容器编排框架可以很好的解决高可用问题
◆ K8S已经成为容器编排的事实标准,能够承载RabbitMQ集群
网络分区故障
◆ 在实际生产中,网络分区是非常常见的故障原因
◆ 网络分区的排查和处理难度较大,需要专门门研究
RabbitMQ状态监控
◆ 在生产环境中,需要实时关注RabbitMQ集群状态
◆ RabbitMQ状态包括流量、内存占用、CPU占用等
使用DockerCompose部署高可用集群
docker 启动 rabbitmq:
docker run -di --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
什么是Docker Compose
◆ Compose是用于定义和运行多容器Docker应用程序的工具
◆ 通过Compose,可以使用YAML文件来配置应用程序需要的所有服务
◆ 使用一个命令,就可以从YAML文件配置中创建并启动所有服务
安装 Docker Compose
- 安装python3-pip dnf install python3-pip
- 安装docker-compose pip3 install docker-compose
- 查看版本 docker-compose version
(由于链接资源是外网,如果安装不成功,提示docker-compose 命令不存在,可线下进行安装)
代码语言:javascript复制cd /apps/rabbitmq/
wget https://github.com/docker/compose/releases/download/1.14.0-rc2/docker-compose-Linux-x86_64
rename docker-compose-Linux-x86_64 docker-compose docker-compose-Linux-x86_64
mv docker-compose /usr/local/bin/docker-compose
chmod x /usr/local/bin/docker-compose
- 使用 Docker Compose 启动3个 RabbitMQ 节点 cd /apps/rabbitmq/ vim docker-compose.yml
version: "2.0"
services:
master:
image: rabbitmq:3-management
hostname: master
ports:
- 5672:5672 #集群内部访问的端口
- 15672:15672 #外部访问的端口
environment:
- RABBITMQ_DEFAULT_USER=guest #用户名
- RABBITMQ_DEFAULT_PASS=guest #密码
- RABBITMQ_ERLANG_COOKIE='ktrabbitmq'
salve1:
image: rabbitmq:3-management
hostname: salve1
ports:
- 5673:5672 #集群内部访问的端口
environment:
- RABBITMQ_ERLANG_COOKIE='ktrabbitmq'
links:
- master
salve2:
image: rabbitmq:3-management
hostname: salve2
ports:
- 5674:5672 #集群内部访问的端口
environment:
- RABBITMQ_ERLANG_COOKIE='ktrabbitmq'
links:
- master
- salve1
salve3:
image: rabbitmq:3-management
hostname: salve3
ports:
- 5675:5672 #集群内部访问的端口
environment:
- RABBITMQ_ERLANG_COOKIE='ktrabbitmq'
links:
- master
- salve1
- salve2
将4个 RabbitMQ 节点搭建为集群
- 启动docker-compose,按照脚本启动集群 docker-compose up -d
- 启动集群后可以看看docker进程是是否启动成功 docker ps
- 进入master节点,启用管控台插件,启动后可在web打开rabbitMQ管控台 docker exec -it rabbitmq_master_1 bash rabbitmq-plugins enable rabbitmq_management 默认用户名: guest 默认密码: guest
- 所有salve关联加入集群 进入salve1节点 docker exec -it rabbitmq_salve1_1 bash 停止salve1节点的rabbitmq rabbitmqctl stop_app 配置salve1节点,加入集群 rabbitmqctl join_cluster rabbit@master 启动salve1节点的rabbitmq rabbitmqctl start_app
进入salve2节点
docker exec -it rabbitmq_salve2_1 bash
停止salve2节点的rabbitmq
rabbitmqctl stop_app
配置salve2节点,加入集群
rabbitmqctl join_cluster rabbit@master
启动salve2节点的rabbitmq
rabbitmqctl start_app
进入salve3节点
docker exec -it rabbitmq_salve3_1 bash
停止salve3节点的rabbitmq
rabbitmqctl stop_app
配置salve3节点,加入集群
rabbitmqctl join_cluster rabbit@master
启动salve3节点的rabbitmq
rabbitmqctl start_app
- 设置镜像模式 进入master内部,执行设置镜像模式即可: docker exec -it rabbitmq_master_1 bash rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
打开管控台:http://192.168.166.134:15672
可看到集群已经搭好,并且所有结点已为镜像模式
使用Kubernetes部署高可用集群
什么是Kubernetes
◆ Kubernetes可以自动化调度、运维Docker容器
◆ Kubernetes已经成为微服务基础架构的“事实标准”
Kubernetes相关概念:
◆ Pod: K8S中的最小业务单元,内含一个或多个容器
◆ StatefulSet: 定义一组有状态Pod,K8S将自动维护
◆ Deployment: 定义一组无状态Pod, K8S将 自动维护
◆ Service: 一组Pod的抽象访问方式,相当于负载均衡器
Kubernetes搭建RabbitMQ集群的脚本
代码语言:javascript复制kind: Service
# 相当于负载均衡层
apiVersion: v1
metadata:
# 元数据
namespace: test-rabbitmq
name: rabbitmq
labels:
app: rabbitmq
type: LoadBalancer
spec:
type: NodePort
ports:
- name: http
protocol: TCP
port: 15672
targetPort: 15672
nodePort: 31672
- name: amqp
protocol: TCP
port: 5672
targetPort: 5672
nodePort: 30672
selector:
app: rabbitmq
---
apiVersion: v1
# 用于注入配置文件
kind: ConfigMap
metadata:
name: rabbitmq-config
namespace: test-rabbitmq
data:
enabled_plugins: |
[rabbitmq_management,rabbitmq_peer_discovery_k8s].
rabbitmq.conf: |
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_k8s
cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
cluster_formation.k8s.address_type = ip
cluster_formation.node_cleanup.interval = 30
cluster_formation.node_cleanup.only_log_warning = true
cluster_partition_handling = autoheal
loopback_users.guest = false
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: rabbitmq
namespace: test-rabbitmq
spec:
serviceName: rabbitmq
replicas: 4
template:
metadata:
labels:
app: rabbitmq
spec:
serviceAccountName: rabbitmq
terminationGracePeriodSeconds: 10
containers:
- name: rabbitmq
image: rabbitmq:3-management
volumeMounts:
- name: config-volume
mountPath: /etc/rabbitmq
ports:
- name: http
protocol: TCP
containerPort: 15672
- name: amqp
protocol: TCP
containerPort: 5672
livenessProbe:
exec:
command: ["rabbitmqctl", "status"]
initialDelaySeconds: 60
periodSeconds: 60
timeoutSeconds: 10
readinessProbe:
exec:
command: ["rabbitmqctl", "status"]
initialDelaySeconds: 20
periodSeconds: 60
timeoutSeconds: 10
imagePullPolicy: Always
env:
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: RABBITMQ_USE_LONGNAME
value: "true"
- name: RABBITMQ_NODENAME
value: "rabbit@$(MY_POD_IP)"
- name: K8S_SERVICE_NAME
value: "rabbitmq"
- name: RABBITMQ_ERLANG_COOKIE
value: "ktrabbit"
volumes:
- name: config-volume
configMap:
name: rabbitmq-config
items:
- key: rabbitmq.conf
path: rabbitmq.conf
- key: enabled_plugins
path: enabled_plugins
由于目前没有Kubernetes的宿主机环境,只能记好笔记,以后再尝试。
网络分区故障
什么是网络分区
网络分区指的是集群分裂为了两个网络“孤岛
RabbitMQ集群网络模型
◆ RabbitMQ集群采用单向环状网络模型
◆ 当网络部分异常时,环状网络异常,相关业务堵塞
RabbitMQ集群网络分区的意义
◆ 此时可以人为造成网络分区,保存部分集群正常运行
集群网络分区处理方法
◆ 手动处理
步骤1:挂起客户端进程
可以减少不必要的消息丢失,如果进程数过多,可跳过
步骤2: 删除镜像队列的配置
如果没有删除镜像队列配置,恢复过程中可能会出现队列漂移
相关命令查看这:https://www.wangt.cc/2020/12/rabbitmq常用命令/
步骤3:挑选信任分区.
挑选的指标有:是否有disk节点 > 分区节点数 > 分区队列数 > 分区客户端连接数
步骤4:关闭非信任区的节点
采用rabbitmqctl stop_ app命令,只关闭RabbitMQ应用,不会关闭ErLang虚拟机.
步骤5:启动非信任区的节点
采用rabbitmqctl start_ app命令,启动过程中伴随着网络的修复
步骤6:检查网络分区是否恢复
- 若已经恢复跳转至步骤8
- 若还存在网络分区进行步骤7
步骤7:重启信任分区中的节点
使用步骤4与5的命令
步骤8:添加镜像队列的配置
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
步骤9:恢复生产者和消费者的进程
若步骤1并未挂起客户端进程,也应该检查客户端连接,必要时重启客户端
◆ 自动处理
RabbitMQ中有3种网络分区自动处理模式:
pause-minority/pause-if-all-down/autoheal
pause-minority:
◆ 发生网络分区时,节点自动检测自己是否处于少数派,若是则关闭自己
◆ 若出现了节点数相同的两个分区,可能会导致两个分区全部关闭
pause-if-all-down:
◆ 每个节点预先配置一个节点列表,当失去和列表中所有节点的通信时,关闭自己.
◆ 此方法考验配置的合理性,配置不合理可能会导致集群节点全部宕机
autoheal:
◆ 发生网络分区时,每个节点使用特定算法自动决定一个“获胜分区",然后重启不在分区的其他节点
◆ 当节点中有关闭状态时,autoheal不会起作用
默认是ignore模式:发生网络分区时,不做任何动作,需要人工介入。
如要开启,配置rabbitmq.config中的cluster parititon handling参数
总结
◆ 集群网络分区有两种处理方式:手动和自动
◆ 手动处理方 式比较考验运维操作水平,但比较常用
◆ 慎用自动处理方式,因为如果配置不合理,会导致更大的问题
RabbitMQ状态监控方式
◆ 通过Java API判断节点是否健康
使用Java应用创建connection与channel
Connection connection = connect ionFac tory. newConnection() ;
Channel channel = connect ion. createChannel() ;
若能创建成功,则节点健康,若创建失败(抛异常)则节点
挂机或与节点的网络连接异常
◆ 通过HTTP Rest API监控集群状态(15672端口)
使用api/nodes/接口获得节点信息
使用api/exchanges/{vhost}/{name}/接口获得exchange状态信息
使用api/queues/{vhost}/{name}/接口获得queue状态信息
◆ 通过监控中间件监控RabbitMQ
常见的监控中间件有Zabbix、Prometheus等
Zabbix、Prometheus的底层原理 是调用HTTP Rest API,再讲数据处理、存储、展示
目前的项目不足之处分析
- 发送消息时无法自动重试 ◆ 消息若发送失败,没有重试处理机制 ◆ 若RabbitMQ集群短暂宕机,消息丢失,业务异常
- 无法得知接收方处理情况 ◆ 发送方无法得知消息是否被处理 ◆ 若消息丢失,业务异常
- 无法自动处理并标记死信 ◆ 死信消息依赖人工处理,需要自动处理并标记 ◆ 消息状态的标记依赖数据库或缓存,对消息状态进行存储
这些不足会在下一篇博文解决
RabbitMQ学习笔记(七)——RabbitMQ分布式事务框架