• Docker 部署 RabbitMQ 集群


    为了保证线上环境RabbitMQ的高可用性,需要部署一套集群环境。RabbitMQ集群主要有两方面的优势:高可用、高性能;高可用就是保证其中部分RabbitMQ节点崩溃或停机的情况下应用程序不受影响,高性能就是对RabbitMQ的横向扩展,支持更大量的消息通信。

    了解 Erlang

    RabbitMQ采用Erlang编写,Erlang和Java类似,它也有虚拟机,每个Erlang实例称为Erlang节点(node),但不同的是多个Erlang应用程序可以运行在同一个节点之上,更特别的是Erlang节点之间可以直接进行本地通信(不管这些节点是否在同一台服务器之上)。

    Erlang cookie

    Erlang 节点通过交换作为令牌的 Erlang cookie来获得认证,由于你一连接到远程节点后,就能执行命令,因此有必要确保该节点是可信的。Erlang将令牌存储在名为 .erlang.cookie 的文件中,该文件一般位于用户的 home 目录下。erlang强制要求
    .erlang.cookie文件只能够被所有者读,也就是文件权限为 400。

    Erlang 节点

    erlang 节点就是 erlang虚拟机实例,启动Erlang节点时可以指定节点名,指定节点名有两个选项,name和sname,name表示长节点名启动,节点完全限定名想 rabbit@hostname.network.tld 这样,如果用短名,就会像这样 rabbit@hostname。RabbitMQ默认启动方式为短节点名。

    Erlang端口映射守护进程epmd(Erlang Port Mapper Daemon)。当你启动一个分布式Erlang节点示,它都会检查本地机器上是否运行着epmd (开机是不会启动的),如果没有,节点就会自动启动epmd 。并自动用epmd 进程进行注册, 提供OS 内核分配的地址和端口。当一台机器上的Erlang节点要和远程节点通信时,本地的epmd 就会通过远程机器上的epmd (默认使用TCP/IP,端口为4369,地址使用的是节点名hotname部分)查询远程节点地址(端口),然后就可以通信啦。

    RabbitMQ 集群

    RabbitMQ 元数据
    • 队列元数据:队列名称和它们的属性(是否可持久化,是否自动删除)
    • 交换机元数据:交换机名称‘类型和属性(可持久化等)
    • 绑定元数据:一张简单展示了如何将消息路由到队列的表格
    • vhost 元数据:为vhost 内的队列、交换机和绑定提供命名空间和安全属性
    集群中的队列

    单节点中,所有关于队列的消息(元数据、状态、内容)都完全存储在该节点上。但是集群中创建的队列,集群只会在单个节点上而不是所有节点上创建完整的队列信息(元数据、状态、内容),只有队列的所有者节点会记录有关队列的所有信息。其他节点只知道队列的元数据和该队列存在的那个节点的指针。所以当节点崩溃时,节点上队列和关联的绑定都消失了,附加在那些队列上的消费者丢失了订阅消息,并且新的消息也会丢失(镜像队列可以解决这个问题)。

    内存节点 VS 磁盘节点

    内存节点将所有的队列、交换机、绑定、用户、权限和 vhost 的元数据仅存储在内存中(速度快、性能高)。而磁盘节点则将元数据存储在磁盘中(相对内存节点性能较弱)。单节点系统只能时磁盘节点,不然RabbitMQ重启后数据就没了。集群中RabbitMQ要求至少一个磁盘节点,当集群中的所有磁盘节点都处于非正常情况时,集群可以继续路由消息,但你无法更改任何东西(创建队列、交换机、绑定;添加用户;更改权限;添加删除节点)。

    搭建集群

    我用两台服务器来完成集群搭建,node1:172.18.219.20;node2:172.18.219.21;这两台服务器已经组成了Docker Swarm,我们直接通过docker stack启动两台 RabbitMQ 实例:

    rabbitmq-stack.ym内容如下:

    version: "3.7"
    services:
      rabbit1:
        image: rabbitmq:3.10.7-management
        hostname: rabbit1
        networks:
          - rabbitmq-network
        ports:
          - target: 15672
            published: 15672
            protocol: tcp
            mode: host
          - target: 5672
            published: 5672
            protocol: tcp
            mode: host
        deploy:
          mode: replicated
          replicas: 1
          update_config:
            parallelism: 1
            delay: 5s
            order: stop-first
          resources:
            limits:
              cpus: '2'
              memory: 2g
            reservations:
              cpus: '0.25'
              memory: 50M
          placement:
            constraints:
              - "node.hostname==node1"
        environment:
          TZ : 'Asia/Shanghai'
          RABBITMQ_NODENAME: rabbit
        volumes:
          - /mnt/rabbitmq/rabbit1:/var/lib/rabbitmq
      rabbit2:
        image: rabbitmq:3.10.7-management
        hostname: rabbit2
        networks:
          - rabbitmq-network
        ports:
          - target: 15672
            published: 15672
            protocol: tcp
            mode: host
          - target: 5672
            published: 5672
            protocol: tcp
            mode: host
        deploy:
          mode: replicated
          replicas: 1
          update_config:
            parallelism: 1
            delay: 5s
            order: stop-first
          resources:
            limits:
              cpus: '2'
              memory: 2g
            reservations:
              cpus: '0.25'
              memory: 50M
          placement:
            constraints:
              - "node.hostname==node2"
        environment:
          TZ : 'Asia/Shanghai'
          RABBITMQ_NODENAME: rabbit
        volumes:
          - /mnt/rabbitmq/rabbit2:/var/lib/rabbitmq
          
    networks:
      rabbitmq-network:
    
    • 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
    • 首先通过 deploy.placement.constaints 分别将rabbit1、rabbit2部署到 node1、node2
    • 分别指定两个容器的 hostname 为 rabbit1、rabbit2
    • 映射端口15672(RabbitMQ Management)、5672(AMQP)
    • 通过环境变量 RABBITMQ_NODENAME 设置 RabbitMQ 节点名称
    • /var/lib/rabbitmq 为 RabbitMQ home 目录和 data 目录,需要映射到宿主机
    • 新版本已经不建议通过环境变量设置 erlang cookie 了,建议在 home 目录下新建 .erlang.cookie 文件,在 每个节点的 .erlang.cookie 写入一致的字符串,注意 .erlang.cookie 文件的权限应该为 400

    使用默认用户名 guest, 默认密码:guest 访问 http://xxxx:15672 登录两个 实例 可以看到目前这样部署后实际上只是两个独立的 RabbitMQ 单节点:
    在这里插入图片描述
    在这里插入图片描述
    后面要做的就是将rabbit2 节点加入到 rabbit1, 这样就完成了集群的搭建。
    进入rabbit2 容器,使用 rabbitmqctl 工具进行操作:

    # 先停止 RabbitMQ 应用程序
    root@rabbit2:/# rabbitmqctl stop_app
    Stopping rabbit application on node rabbit@rabbit2 ...
    # 重置节点
    root@rabbit2:/# rabbitmqctl reset
    Resetting node rabbit@rabbit2 ...
    # 加入 rabbit1, rabbit@rabbit1 就是erlang 的节点名,在这里会通过docker dns 找到 rabbit1(service name)
    # 这一步如果失败,很可能时 erlang cookie 有问题,不一致或者权限只不为 400
    root@rabbit2:/# rabbitmqctl join_cluster rabbit@rabbit1
    Clustering node rabbit@rabbit2 with rabbit@rabbit1
    00:51:49.386 [warning] Feature flags: the previous instance of this node must have failed to write the `feature_flags` file at `/var/lib/rabbitmq/mnesia/rabbit@rabbit2-feature_flags`:
    00:51:49.386 [warning] Feature flags:   - list of previously disabled feature flags now marked as such: [:maintenance_mode_status]
    00:51:49.563 [warning] Feature flags: the previous instance of this node must have failed to write the `feature_flags` file at `/var/lib/rabbitmq/mnesia/rabbit@rabbit2-feature_flags`:
    00:51:49.563 [warning] Feature flags:   - list of previously enabled feature flags now marked as such: [:maintenance_mode_status]
    00:51:49.581 [error] Failed to create a tracked connection table for node :rabbit@rabbit2: {:node_not_running, :rabbit@rabbit2}
    00:51:49.581 [error] Failed to create a per-vhost tracked connection table for node :rabbit@rabbit2: {:node_not_running, :rabbit@rabbit2}
    00:51:49.581 [error] Failed to create a per-user tracked connection table for node :rabbit@rabbit2: {:node_not_running, :rabbit@rabbit2}
    # 启动 rabbit2 节点的 RabbitMQ 应用
    root@rabbit2:/# rabbitmqctl start_app
    Starting node rabbit@rabbit2 ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    再次查看 RabbitMQ Management:
    在这里插入图片描述
    在这里插入图片描述
    到此就完成了集群搭建

    镜像队列

    虽然集群搭建完成了,但是 RabbitMQ 默认情况下队列只会存在单节点是,当某个节点故障时,会导致这个节点上的所有队列都不再可用, 镜像队列就可以保证 RabbitMQ 的高可用,镜像队列可以参考该Blog

    压力测试

    RabbitMQ 团队提供的 PerfTest 可以对 RabbitMQ 进行压力测试,可以参考该Blog

  • 相关阅读:
    在VScode中如何将界面语言设置为中文
    django开发个人博客系统
    Java实现ip地址计算
    【机器学习-周志华】学习笔记-第八章
    CCAK—云审计知识证书学习
    LeetCode //C - 210. Course Schedule II
    maven打完包运行,测试结果和本地不一致
    科创人·连界董事长王玥:错误的成功约等于失败,创新服务要与企业共命运
    Python Joblib库使用学习总结
    前端开发必看:高效调试技巧大揭秘
  • 原文地址:https://blog.csdn.net/i_love_t/article/details/126439314