RabbitMQ学习笔记(六)——优化RabbitMQ集群

目录

  • 优化RabbitMQ集群
    • 什么是真正的高可用
    • 网络分区故障
    • RabbitMQ状态监控
  • 使用DockerCompose部署高可用集群
    • 什么是Docker Compose
    • 安装 Docker Compose
    • 将4个 RabbitMQ 节点搭建为集群
  • 使用Kubernetes部署高可用集群
    • 什么是Kubernetes
    • Kubernetes相关概念:
    • Kubernetes搭建RabbitMQ集群的脚本
  • 网络分区故障
    • 什么是网络分区
    • RabbitMQ集群网络模型
    • RabbitMQ集群网络分区的意义
    • 集群网络分区处理方法
      • ◆ 手动处理
      • ◆ 自动处理
    • 总结
  • RabbitMQ状态监控方式
  • 目前的项目不足之处分析

优化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

  1. 安装python3-pip
    dnf install python3-pip
  2. 安装docker-compose
    pip3 install docker-compose
  3. 查看版本
    docker-compose version

(由于链接资源是外网,如果安装不成功,提示docker-compose 命令不存在,可线下进行安装)

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
  1. 使用 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 节点搭建为集群

  1. 启动docker-compose,按照脚本启动集群
    docker-compose up -d

  2. 启动集群后可以看看docker进程是是否启动成功
    docker ps

  3. 进入master节点,启用管控台插件,启动后可在web打开rabbitMQ管控台
    docker exec -it rabbitmq_master_1 bash
    rabbitmq-plugins enable rabbitmq_management
    默认用户名: guest 默认密码: guest

  4. 所有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

  1. 设置镜像模式
    进入master内部,执行设置镜像模式即可:
    docker exec -it rabbitmq_master_1 bash
    rabbitmqctl set_policy ha-all “^” ‘{“ha-mode”:“all”}’

打开管控台:http://192.168.166.134:15672
可看到集群已经搭好,并且所有结点已为镜像模式
RabbitMQ学习笔记(六)——优化RabbitMQ集群_第1张图片

使用Kubernetes部署高可用集群

什么是Kubernetes

◆ Kubernetes可以自动化调度、运维Docker容器
◆ Kubernetes已经成为微服务基础架构的“事实标准”
RabbitMQ学习笔记(六)——优化RabbitMQ集群_第2张图片

Kubernetes相关概念:

◆ Pod: K8S中的最小业务单元,内含一个或多个容器
◆ StatefulSet: 定义一组有状态Pod,K8S将自动维护
◆ Deployment: 定义一组无状态Pod, K8S将 自动维护
◆ Service: 一组Pod的抽象访问方式,相当于负载均衡器

Kubernetes搭建RabbitMQ集群的脚本

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集群_第3张图片

RabbitMQ集群网络模型

◆ RabbitMQ集群采用单向环状网络模型
RabbitMQ学习笔记(六)——优化RabbitMQ集群_第4张图片

◆ 当网络部分异常时,环状网络异常,相关业务堵塞
RabbitMQ学习笔记(六)——优化RabbitMQ集群_第5张图片

RabbitMQ集群网络分区的意义

◆ 此时可以人为造成网络分区,保存部分集群正常运行
RabbitMQ学习笔记(六)——优化RabbitMQ集群_第6张图片

集群网络分区处理方法

◆ 手动处理

步骤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学习笔记(六)——优化RabbitMQ集群_第7张图片

总结

◆ 集群网络分区有两种处理方式:手动和自动
◆ 手动处理方 式比较考验运维操作水平,但比较常用
◆ 慎用自动处理方式,因为如果配置不合理,会导致更大的问题

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,再讲数据处理、存储、展示

目前的项目不足之处分析

  1. 发送消息时无法自动重试
    ◆ 消息若发送失败,没有重试处理机制
    ◆ 若RabbitMQ集群短暂宕机,消息丢失,业务异常

  2. 无法得知接收方处理情况
    ◆ 发送方无法得知消息是否被处理
    ◆ 若消息丢失,业务异常

  3. 无法自动处理并标记死信
    ◆ 死信消息依赖人工处理,需要自动处理并标记
    ◆ 消息状态的标记依赖数据库或缓存,对消息状态进行存储

这些不足会在下一篇博文解决
RabbitMQ学习笔记(七)——RabbitMQ分布式事务框架

你可能感兴趣的:(RabbitMQ,rabbitmq,学习,docker)