RabbitMQ cluster

RabbitMQ 集群

  1. RabbitMQ始终会记录以下四种内部元数据

    1
    2
    3
    4
    5
    6
    7
    * 队列元数据(队列名称和它们的属性)
    * 交换器元数据(交换器名称,类型和属性)
    * 绑定元数据(一张简单的表格展示了如何将消息路由到队列)
    * vhost元数据(为vhost内部队列,交换器和绑定提供命名空间和安全属性)
    *注意:
    单一节点,RabbitMQ会将所有信息存储在内存,同时将那些标记为可持久化的队列和交换器存储到硬盘上
    引入集群,RabbitMQ需要追踪新的元数据类型:集群节点位置,以及节点与已记录的其他类型元数据的关系;集群也提供了选择,将元数据存储到磁盘或者内存中*
  2. 集群中的队列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    将两个节点组成集群,如果创建队列,集群只会在单个节点而不是所有节点上创建完整的队列信息(元数据,状态,内容),只有队列的所有者知道有关队列的所有信息
    所有其他非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针.因此,当集群节点崩溃时,该节点的队列和关联绑定都消失了.
    *注意: 只有队列在最开始没有被设置可持久化时,集群节点崩溃才可以让消费者重连并重新创建队列;如果队列开始已经设置为可持久化,客户端重新声明会得到404错误,这样确保了当失败节点恢复后加入集群,该节点上的队列不会丢失.想要指定队列重回集群的唯一方法是恢复故障节点.*

    为什么默认情况下RabbitMQ不将队列内容和状态复制到所有节点:
    * 存储空间 如果每个集群节点都拥有所有队列的完全拷贝,那么新计入了节点不会给你带来更多存储空间
    * 性能 消息的发布需要将消息复制到每一个集群节点,对持久化消息来说,每一条消息都会触发磁盘IO.每次新节点添加,网络和磁盘负载都会增加

    通过设置集群中唯一节点来负责任何特定队列,只有该负责节点才会因队列消息而消耗磁盘IO.所有其他节点需要将接收到该队列的请求转发给该队列的所有者节点.因此,增加Rabbit集群节点意味着你将拥有更多的节点传播队列,这些新节点为你带来了性能提升
  3. 分布交换器

    1
    2
    3
    4
    交换器不同队列那样拥有自己的进程,交换器说到底只是一个名称和一个队列绑定列表
    当你将消息发布到交换器时,实际上是由你所连接到的信道将消息上的路由键同交换器的绑定列表进行比较,然后路由信息.正是信道按照绑定的匹配结果,将消息路由到队列
    理解信道是真正的路由器这一点很重要,这解释了为什么交换器不会像集群中的队列那样受到相同的限制
    由于交换器只不过是一张查询表,而非实际上的消息路由器,因此将交换器在整个集群中进行复制会更加简单.
  4. 是内存节点,还是磁盘节点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    每个RabbitMQ节点,不管是单一节点还是集群中的一部分,要么是内存节点,要么是磁盘节点
    * 内存节点将所有的队列,交换器,绑定,用户,权限和vhost的元数据定义都仅存储在内存中
    * 磁盘节点则将元数据存储在磁盘
    *注意:单节点只允许磁盘类型节点,否则你每次重启RabbitMQ,所有关于系统的配置信息都会丢失*
    在集群中,你可以选择配置部分节点为内存节点,因为它使得向队列和交换器声明之类的操作更加快速
    *注意: 当在集群中声明队列,交换器或绑定时,这些操作直到所有集群节点都成功提交元数据变更后才返回。对内存节点来说,将变更写入内存;对磁盘节点来说,将变更写入磁盘,直到完成之后*

    RabbitMQ只要求在集群中至少有一个磁盘节点,所有其他节点都可以是内存节点.当节点加入和离开集群时,它们必须将该变更通知到至少一个磁盘节点
    如果集群只有一个磁盘节点,正好它又崩溃了,那么集群还可以路由消息,但不能操作:创建队列,创建交换器,创建绑定,添加用户,更改权限,添加或删除集群节点
    解决方法: 建议在集群中设置两台磁盘节点
    *注意: 只有一个需要所有的磁盘节点必须在线的操作是添加或者删除集群节点*

    当重启内存节点后,它们会连接到预先配置的磁盘节点,下载当前集群元数据拷贝.如果你只讲两个磁盘节点中的一个高速了内存节点,不凑巧这个磁盘节点崩溃了,内存节点重启后就无法找到集群了.所以添加内存节点时,确保告知其所有的磁盘节点.只要内存节点可以找到至少一个磁盘节点,那么它就能在重启后重新加入集群
  5. 建立集群

    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
    集群一: 单台集群启动三个Rabbit服务
    1. 启动三个Rabbit服务
    RABBITMQ_NODE_PORT=5671 RABBITMQ_NODENAME=rabbit_1 rabbitmq-server -detached
    RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit_2 rabbitmq-server -detached
    RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit_3 rabbitmq-server -detached
    2. 停止第二个节点上的RabbitMQ程序
    rabbitmqctl -n rabbit_2@localhost stop_app
    3. 重新设置第二个节点的元数据和状态为清空状态
    rabbitmqctl -n rabbit_2@localhost reset
    4. 将第二个节点加入到第一个集群节点
    rabbitmqctl -n rabbit_2@localhost cluster rabbit_1@localhost rabbit_2@localhost
    * -n rabbit_2@localhost 表示你想在指定的节点上执行命令
    * cluster rabbit_1@localhost rabbit_2@localhost 表示rabbit_1和将要加入的rabbit_2都是磁盘节点
    5. 重新启动第二个节点
    rabbitmqctl -n rabbit_2@localhost start_app
    6. 将第三个节点加入到集群
    rabbitmqctl -n rabbit_3@localhost stop_app
    rabbitmqctl -n rabbit_3@localhost reset
    rabbitmqctl -n rabbit_3@localhost cluster rabbit_1@localhost rabbit_2@localhost
    rabbitmqctl -n rabbit_3@localhost start_app
    *注意: 将第三个节点加入到集群时,cluster后面并没有包含rabbit_3,表示为rabbit_3指定了两个磁盘节点rabbit_2和rabbit_1,rabbit_3将会成为内存节点*
    7. 查看集群状态
    rabbitmqctl cluster_status
    查看nodes部分:
    * disc 表示磁盘节点信息
    * ram 表示内存节点信息
    * running_nodes 告诉你集群中的哪些节点正在运行
    8. 连接到RabbitMQ
    你可以连接到Running_nodes中任何一个节点,开始创建队列,发布消息,或者执行其他AMQP命令

    集群二: 将节点分布到更多机器上(三台机器为例)
    1. 在三台机器上启动RabbitMQ
    RABBITMQ_NODE_PORT=5671 RABBITMQ_NODENAME=rabbit_1 rabbitmq-server -detached
    RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit_2 rabbitmq-server -detached
    RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit_3 rabbitmq-server -detached
    2. 将其中一台的RabbitMQ的Erlang cookie复制到其他几个节点
    /var/lib/rabbitmq/.erlang.cookie
    复制cookie内的字符串,并粘贴到其他两个节点的/var/lib/rabbitmq/.erlang.cookie中
    重启其他另外两个节点
    rabbitmqctl stop
    rabbitmqctl start
    3. 停止第二个节点上的RabbitMQ程序
    rabbitmqctl -n rabbit_2@localhost stop_app
    4. 重新设置第二个节点的元数据和状态为清空状态
    rabbitmqctl -n rabbit_2@localhost reset
    5. 将第二个节点加入到第一个集群节点
    rabbitmqctl -n rabbit_2@localhost cluster rabbit_1@localhost rabbit_2@localhost
    * -n rabbit_2@localhost 表示你想在指定的节点上执行命令
    * cluster rabbit_1@localhost rabbit_2@localhost 表示rabbit_1和将要加入的rabbit_2都是磁盘节点
    6. 重新启动第二个节点
    rabbitmqctl -n rabbit_2@localhost start_app
    7. 将第三个节点加入到集群
    rabbitmqctl -n rabbit_3@localhost stop_app
    rabbitmqctl -n rabbit_3@localhost reset
    rabbitmqctl -n rabbit_3@localhost cluster rabbit_1@localhost rabbit_2@localhost
    rabbitmqctl -n rabbit_3@localhost start_app
    *注意: 将第三个节点加入到集群时,cluster后面并没有包含rabbit_3,表示为rabbit_3指定了两个磁盘节点rabbit_2和rabbit_1,rabbit_3将会成为内存节点*
    8. 查看集群状态
    rabbitmqctl cluster_status
    查看nodes部分:
    * disc 表示磁盘节点信息
    * ram 表示内存节点信息
    * running_nodes 告诉你集群中的哪些节点正在运行
    9. 连接到RabbitMQ
    你可以连接到Running_nodes中任何一个节点,开始创建队列,发布消息,或者执行其他AMQP命令

    集群三:将节点从集群中移除
    1. 找到你需要停止的集群节点
    2. 移除
    rabbitmqctl -n rabbit_3@localhost stop_app
    rabbitmqctl -n rabbit_3@localhost reset
    3. 验证
    方法一: 启动移除的节点
    rabbitmqctl -n rabbit_3@localhost start_app
    rabbitmqctl -n rabbit_3@localhost cluster_status
    方法二: 查看已有集群的状态
    rabbitmqctl -n rabbit_1@localhost cluster_status
  6. 升级版本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    在独立的系统中升级新版本RabbitMQ比较容易: 解压新版本,然后停服,替换新版本,启动服务.

    集群升级版本是半自动化的,不能简单的将新版本在集群节点上解压并重启服务:
    1. 通过RabbitMQ Management 插件备份当前配置
    2. 关闭所有生产者并等待消费者消费完队列中的所有消息
    3. 关闭节点,并解压新版本RabbitMQ到安装目录
    4. 选择其中一个磁盘节点作为升级节点,当启动时,该节点会将持久化的集群数据升级到新版本
    5. 然后启动其他集群的磁盘节点,它们会获取升级后的集群数据
    6. 最后启动集群的内存节点
  7. 镜像队列和保留消息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    RabbitMQ集群默认情况下队列只存活于集群中的一个节点上.在RabbitMQ 2.6.0版本之后,Rabbit增加了内建的双活冗余选项: 镜像队列
    镜像队列: 像普通队列一样,镜像队列的主拷贝仅存在于一个节点上,但与普通队列不同的是,镜像节点在集群中的其他节点上拥有从队列拷贝.一旦队列的主节点不可用,最老的从队列将被选举为新的主队列

    例如:
    queue_args = {"x-ha-policy":"all"}
    channel.queue_declare(queue="hello-queue",arguments=queue_args)

    由于队列的镜像性质是由应用程序在允许时指定的,RabbitMQ团队决定你可以同样的方式在允许时指定队列需要镜像到哪些节点.
    指定将镜像放在哪个节点:
    queue_args = {"x-ha-policy":"nodes","x-ha-policy-params":["rabbit@localhost"]}
    channel.queue_declare(queue="hello-queue",arguments=queue_args)

    *注意: 新增的从拷贝只会包含那些其添加进来之后从镜像队列发来的消息.RabbitMQ不会将镜像队列现已经存在的内容与新添加的从拷贝进行同步. 新增的从拷贝最终会和现存的队列拷贝拥有相同的状态.*

RabbitMQ从故障中恢复

1
2
3
4
5
1.架构的规划
haproxy
RabbitMQ集群
2.业务代码的调整
错误检测和重新链接