首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RabbitMQ集群

RabbitMQ集群

作者头像
shysh95
发布2019-07-23 09:54:25
7810
发布2019-07-23 09:54:25
举报
文章被收录于专栏:shysh95shysh95

本文主要讲解以下内容

  1. 集群搭建
  2. 单节点故障恢复
  3. 集群迁移
  4. 集群监控

本文主要从运维的角度来保证RabbitMQ服务的高可用,避免单点故障问题。

集群搭建

首先一个点,明白为什么要搭建集群。搭建集群的目的主要是为了避免单点故障,提高系统的高可用和性能的线性扩展。

节点类型

RabbitMQ集群中有两种节点类型,一种disk(磁盘)节点,一种ram(内存)节点。内存节点将队列、交换器、绑定关系、权限和vhost的元数据定义都存储在内存中,磁盘节点将这些信息存储在磁盘上。

RabbitMQ在创建队列、交换器、和绑定关系的时候,需要等集群所有的节点都成功提交元数据变更后才返回。内存节点可以为集群提供出色的性能,因为写入内存比写入磁盘快的不是一点半点,磁盘节点为集群提供了高可靠性。

RabbitMQ要求集群中至少有一个磁盘节点,其他节点都可以是内存节点。假设磁盘节点故障了,此时只能发送和消费消息,不能进行队列创建、交换器创建、绑定关系、用户,以及更改权限、添加和删除集群节点的操作。所以在建立集群的时候尽量保证多个磁盘节点的存在,其实在队列、交换器、绑定关系变化较小的RabbitMQ集群中,可以考虑将所有节点设置为磁盘节点。

集群搭建

集群搭建的方式主要有多机多节点和单机多节点,单机多节点可以用在测试环境中,不太适合生产。这里就着重讲多机多节点部署RabbitMQ集群。

RabbitMQ每一个节点都会存储队列、交换器等的元数据信息,但是消息的内容只会落到一个节点,假设其中一个节点宕机,该节点上的消息将会全部丢失,所以RabbitMQ集群并不能完全保证消息万无一失。

RabbitMQ节点备份所有的元数据信息主要包括以下内容:

  • 队列元数据:名称和属性
  • 交换器:名称及属性
  • 绑定关系元数据:交换器与队列或者交换器与交换器之间的绑定关系
  • vhost元数据:为vhost内的队列、交换器和绑定提供命名空间及安全属性

之所以不会备份消息是基于空间和性能的考虑,RabbitMQ集群创建队列的时候,只会在单个节点创建队列的进程并包含完整的队列信息,其他节点只知道队列的元数据和指向该队列存在的那个节点的指针。

因此当节点崩溃时,该节点上的队列进程和关联的绑定都会消失,队列上的消费者会丢失消息,且任何匹配该队列的消息也会丢失。

交换器不同于队列,只是一个名称和绑定列表。当创建一个新的交换器时,RabbitMQ只需要将绑定列表添加到集群中的所有节点。

多机多节点部署

启动三个RabbitMQ容器,rabbit001、rabbit002、rabbit003均为磁盘节点。

我们采用容器部署集群,首要问题是节点互联,节点互联方式有两种:

  • 私有DNS
  • Docker Networking:使用Docker network创建私有网络,连接到网络内的节点可以互联

我们采用第二种方式解决集群单机多容器节点的互联关系,多机的话大家可以自己去研究一下overlay。

创建节点互联基础
# 创建私有网络docker newtwork create rabbitmqnet
创建节点

先创建一个目录,用来备份容器中的数据,这样以后删除容器时,使用新的容器也可以将旧容器的数据加载。注意这个目录的权限一定要给够。

mkdir /usr/local/docker_app

运行三个RabbitMQ服务容器

# 创建第一节点docker run -d \        --name=rabbit001 \        -p 5672:5672 \        -p 15672:15672 \        -e RABBITMQ_NODENAME=rabbit001 \        -e RABBITMQ_ERLANG_COOKIE='rabbitmqcookie' \        -v /usr/local/docker_app/rabbitmq/rabbit001/data:/var/lib/rabbitmq \        -h rabbit001 \        --net rabbitmqnet \        rabbitmq:management# 创建第二个节点docker run -d \        --name=rabbit002 \        -p 5673:5672 \        -p 15673:15672 \        -e RABBITMQ_NODENAME=rabbit002 \        -e RABBITMQ_ERLANG_COOKIE='rabbitmqcookie' \        -v /usr/local/docker_app/rabbitmq/rabbit002/data:/var/lib/rabbitmq \        -h rabbit002 \        --net rabbitmqnet \        rabbitmq:management# 创建第三个节点        docker run -d \        --name=rabbit003 \        -p 5674:5672 \        -p 15674:15672 \        -e RABBITMQ_NODENAME=rabbit003 \        -e RABBITMQ_ERLANG_COOKIE='rabbitmqcookie' \        -v /usr/local/docker_app/rabbitmq/rabbit003/data:/var/lib/rabbitmq \        -h rabbit003 \        --net rabbitmqnet \        rabbitmq:management

将节点2(rabbit002)和节点3(rabbit003)加入到节点1(rabbit001)的集群

# 进入rabbit002容器# 停止RabbitMQ服务# 重置RabbitMQ服务# 将其加入rabbit001的集群# 重启RabbitMQ服务docker exec rabbit002 bash -c \        "rabbitmqctl stop_app && \         rabbitmqctl reset && \         rabbitmqctl join_cluster rabbit001@rabbit001 && \         rabbitmqctl start_app"# 进入rabbit003容器# 停止RabbitMQ服务# 重置RabbitMQ服务# 将其加入rabbit001的集群# 重启RabbitMQ服务docker exec rabbit003 bash -c \        "rabbitmqctl stop_app && \         rabbitmqctl reset && \         rabbitmqctl join_cluster rabbit001@rabbit001 && \         rabbitmqctl start_app"# 查看集群状态rabbitmqctl cluster_status

查看集群状态会返回下图信息

如果关闭了集群中的所有节点,则需要确保在启动的时候最后关闭的那个节点是第一个启动的。如果第一个启动的不是最后关闭的节点,那么这个节点会等待最后关闭的节点启动。

这个等待时间是30秒,如果没有等到,那么这个先启动的节点也会失败。在最新的版本中会有重试机制,默认重试10次30秒以等待最后关闭的节点启动。

# 修改节点类型# type有两个值:disc、ramrabbitmqctl change_cluster_node_type {type}# 剔除单个节点# 在关闭集群中的每个节点之后,如果最后一个关闭的节点最终由于某些异常而无法启动,# 则可以通过rabbitmqctl forget_cluster_node命令来将此节点剔除出当前集群。# 进入节点2容器# 停止节点3容器,并且将节点3剔除到集群docker exec -it rabbit002 bashrabbitmqctl -n rabbit003@rabbit003 stop_app && rabbitmqctl forget_cluster_node rabbit003@rabbit003

集群节点升级

集群节点升级的过程中主要要确保一个点:确保原节点的Mnesia中的数据不被变更,且新节点中的Mnesia路径的指向要与原节点中的相同。RabbitMQ集群由多个节点组成。具体步骤:

  1. 关闭所有节点的服务,注意采用rabbitmqctl stop命令关闭。
  2. 保存各个节点的Mnesia数据(上面容器启动时使用了-v参数,将容器内的目录绑定到了宿主机下面)。
  3. 使用新版本的RabbitMQ镜像创建容器,保证-v的路径不变(这边是为了保证Mensia路径不变)。
  4. 启动新版本的容器,注意先重启原版本中最后关闭的那个节点 。

集群迁移

RabbitMQ集群迁移包括元数据重建、数据迁移,以及与客户端连接的切换。

元数据重建

可以通过将元数据文件导出为json,然后再导入到新集群中去,这种只适合可以访问web界面的情况。而且新旧版本如果不兼容,就会导入失败。

下面是json文件的格式。

{    "rabbit_version":"3.7.12",    "users":[        {            "name":"guest",            "password_hash":"BdRgHh8vezii4gceD0kBIAF+qcN4ROqEBRjES0SNxXytw5FW",            "hashing_algorithm":"rabbit_password_hashing_sha256",            "tags":"administrator"        }    ],    "vhosts":[        {            "name":"/"        }    ],    "permissions":[        {            "user":"guest",            "vhost":"/",            "configure":".*",            "write":".*",            "read":".*"        }    ],    "topic_permissions":[    ],    "parameters":[    ],    "global_parameters":[        {            "name":"cluster_name",            "value":"rabbit001@rabbit001"        }    ],    "policies":[    ],    "queues":[    ],    "exchanges":[    ],    "bindings":[    ]}

这种元数据重建方式主要有以下几个问题:

  • 必须打开web管理插件的支持
  • 可能出现新老版本不兼容问题
  • 队列在新集群中会全部在一个节点重建,造成负载不均。

更加晚上的方式元数据重建方式是通过HTTP API接口创建相应的数据。

数据迁移和客户端连接的切换

完成元数据的重建,下面就是数据迁移和客户端连接切换工作。

生产者只需要断开新客户端的连接,然后连接新的RabbitMQ集群即可。

消费者则需要考虑消费消息的情况,一种是等原来队列的消息全部消费完,然后切换新集群。还有一种情况就是原集群不可用需要立即切换到新集群,而此时的就需要处理原集群中的消息。

这时的处理办法是通过一个应用程序将消息取出来进行缓存,然后另一个程序取缓存中的消息发送到新集群中的队列中去。RabbitMQ本身提供的Federation和Shovel插件都可以实现此功能。

集群监控

主要通过HTTP API来实现

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员修炼笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 集群搭建
    • 节点类型
      • 集群搭建
        • 多机多节点部署
    • 集群节点升级
    • 集群迁移
      • 元数据重建
        • 数据迁移和客户端连接的切换
        • 集群监控
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档