zl程序教程

您现在的位置是:首页 >  工具

当前栏目

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

2023-06-13 09:11:07 时间

优化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 可看到集群已经搭好,并且所有结点已为镜像模式

使用Kubernetes部署高可用集群

什么是Kubernetes

◆ Kubernetes可以自动化调度、运维Docker容器 ◆ Kubernetes已经成为微服务基础架构的“事实标准”

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集群采用单向环状网络模型

◆ 当网络部分异常时,环状网络异常,相关业务堵塞

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

目前的项目不足之处分析

  1. 发送消息时无法自动重试 ◆ 消息若发送失败,没有重试处理机制 ◆ 若RabbitMQ集群短暂宕机,消息丢失,业务异常
  2. 无法得知接收方处理情况 ◆ 发送方无法得知消息是否被处理 ◆ 若消息丢失,业务异常
  3. 无法自动处理并标记死信 ◆ 死信消息依赖人工处理,需要自动处理并标记 ◆ 消息状态的标记依赖数据库或缓存,对消息状态进行存储

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