跳至主要内容
版本:3.13

将 RabbitMQ 镜像经典队列迁移到仲裁队列

哪种更好:镜像经典队列还是仲裁队列?仲裁队列 是更好的选择,并且将是RabbitMQ 4.0 版本开始的唯一选择。此信息解释了原因,您应该从镜像经典队列迁移到仲裁队列的原因,迁移期间处理功能的方法,以及您可能采用的某些迁移路线的步骤。

您应该迁移到镜像经典队列的原因如下

  • RabbitMQ 3.9 版本弃用了经典镜像队列。它们将在 RabbitMQ 4.0 版本中完全移除。
  • 仲裁队列在几乎所有用例中都能够承受更高的吞吐量。仲裁队列能够承受 30000 条消息的吞吐量(使用 1kb 消息),同时提供高水平的数据安全性,并将数据复制到集群中的所有 3 个节点。经典镜像队列的吞吐量只有此吞吐量的三分之一,并且提供更低的数据安全性。
  • 仲裁队列更可靠,对于大多数工作负载来说更快,并且需要很少的维护。

但是,在迁移到仲裁队列之前,需要考虑一些事项。

  • 虽然仲裁队列与镜像经典队列相比是一种更好的队列类型,但它们在功能方面并不完全兼容。当您决定是否要从镜像经典队列迁移到仲裁队列时,建议先查看仲裁队列文档,您可以查看功能矩阵表,其中提供两种队列类型的比较(镜像经典队列与仲裁队列)。

  • 从镜像经典队列迁移到仲裁队列的复杂程度取决于镜像经典队列当前正在使用的功能。某些功能需要更改队列的使用方式(请参阅需要更改队列使用方式的镜像经典队列功能),而其他功能只需要从源代码中删除该功能或将其移至策略(请参阅可以从源代码中删除或移至策略的镜像经典队列功能)。

  • 还需要注意的是,迁移后的应用程序应该在仲裁队列上进行全面测试,因为行为在负载和边缘情况下可能会不同。

决定要采用的迁移路线:兼容性注意事项

不兼容的功能可以在策略或源代码中引用。RabbitMQ 严格验证队列声明和消费的参数。因此,为了迁移,您必须清理源代码中所有关于不兼容功能的信息。对于某些功能,需要更改队列的使用方式,请参阅需要更改队列使用方式的镜像经典队列功能。对于其他功能,只需从源代码中删除相应的字符串或将该功能移至策略即可,请参阅可以从源代码中删除或移至策略的镜像经典队列功能

与镜像相关的通用策略和参数为:ha-modeha-params ha-sync-modeha-promote-on-shutdownha-promote-on-failurequeue-master-locator

有几种迁移路径可用

  • 蓝绿部署
  • 按虚拟主机迁移队列 可能是您能采取的最有效的迁移路径,如果它是可行的选择。如果所有不兼容的功能都被清理或移至策略,则现有代码应该能够同时使用镜像经典队列和仲裁队列。您只需要更改连接参数以连接到您为仲裁队列创建的新虚拟主机。
  • 就地迁移 表示您重新使用相同的虚拟主机。在迁移进行时,您必须能够停止给定队列的所有消费者和生产者。

在决定可以使用哪种迁移方法之前,您必须先找到镜像经典队列及其正在使用的功能。

查找要迁移的镜像经典队列

要查找必须迁移的镜像经典队列,请运行以下脚本(该脚本使用 rabbitmqctl 在所有虚拟主机上计算所有队列,以制表符分隔的值形式输出)。

注意,以下命令使用 effective_policy_definition 参数,该参数仅在 RabbitMQ 3.10.13/3.11.5 及更高版本中可用。如果该参数不可用,您可以使用 3.10.13/3.11.5 之后任何 RabbitMQ 版本的 rabbitmqctl,或手动将策略名称与定义匹配。

#!/bin/sh
printf "%s\t%s\t%s\n" vhost queue_name mirrors
for vhost in $(rabbitmqctl -q list_vhosts | tail -n +2) ; do
rabbitmqctl -q list_queues -p "$vhost" name durable policy effective_policy_definition arguments mirror_pids type |
sed -n '/\t\[[^\t]\+\tclassic$/{s/\t\[[^\t]\+\tclassic$//; p}' |
xargs -x -r -L1 -d '\n' printf "%s\t%s\n" "$vhost"
done

所有在其有效策略定义中包含 ha-mode 的镜像经典队列都必须迁移到其他类型的队列。所有这些队列在管理 UI 和 CLI 中都列为镜像经典队列。通过运行以下脚本查找应用该策略的策略。

#!/bin/sh
printf "%s\t%s\t%s\t%s\t%s\t%s\n" vhost policy_name pattern apply_to definition priority
for vhost in $(rabbitmqctl -q list_vhosts | tail -n +2) ; do
rabbitmqctl -q list_policies -p "$vhost" |
grep 'ha-mode'
done

需要更改队列使用方式的镜像经典队列功能

当镜像经典队列使用以下一项或多项功能时,无法直接迁移到仲裁队列。应用程序与代理的交互方式需要更改。此信息解释了如何查找运行系统中是否正在使用其中一些功能,以及您必须进行的更改以使迁移更容易。

优先级队列

要确定经典镜像队列是否使用“优先级”功能,您可以查看查找要迁移的镜像经典队列中运行的命令提供的队列输出列表中的 x-max-priority 字符串,或者您也可以在源代码中搜索 x-max-priority 字符串。有关优先级实现方式的更多信息,请转到优先级队列支持

优先级队列不是使用策略创建的,因此在迁移它们时不需要进行策略更改。经典镜像队列在幕后为每个优先级创建单独的队列。要迁移使用“优先级”功能的单个镜像经典队列,您必须创建所需数量的仲裁队列。创建仲裁队列后,请相应地调整这些新仲裁队列的发布和消费。

队列长度限制溢出设置为 reject-publish-dlx

仲裁队列不支持 overflow 设置为reject-publish-dlx 的队列长度超过限制。不支持 reject-publish-dlx 值。

对于镜像经典队列,将消息发布到具有 reject-publish-dlx 设置的已满队列会导致 RabbitMQ 将被拒绝的消息重新发布到死信交换机。对于仲裁队列,要应用相同的逻辑,您必须将 reject-publish-dlx 更改为 reject-publish。然后,处理负确认:在收到负确认后,应用程序必须将消息重新发布到不同的交换机。

要确定是否为要迁移的镜像经典队列配置了 overflow 设置为 reject-publish-dlx,请查看查找要迁移的镜像经典队列中运行的命令提供的队列输出列表中的 reject-publish-dlx 字符串,或者您也可以在源代码中搜索 reject-publish-dlx 字符串。

消费者的全局 QoS

仲裁队列不支持全局QoS 预取,其中通道为使用该通道的所有消费者设置单个预取限制。如果需要此功能,请尝试使用其他方法来实现相同的结果,例如,一种解决方案可能是使用更低的每个消费者 QoS(根据已知应用程序负载模式)。

要确定是否正在使用此功能,请在运行系统上运行以下命令并检查是否输出非空内容。

rabbitmqctl list_channels pid name global_prefetch_count | sed -n '/\t0$/!p'

将返回已启用全局 QoS 的通道 PID 列表。然后,运行以下命令将通道 PID 映射到队列名称,以验证它是否为镜像经典队列。

rabbitmqctl list_consumers queue_name channel_pid

消费者的 x-cancel-on-ha-failover

经典镜像队列消费者可以在队列领导者故障转移时自动取消。这会导致有关哪些消息发送到哪个消费者的信息丢失,并导致相同的消息再次发送(重复消息)。

x-cancel-on-ha-failover 涵盖了重复消息的某些情况,但其他情况则没有涵盖。x-cancel-on-ha-failover 涵盖的大多数情况在仲裁队列中不存在,但那些没有涵盖的情况仍然存在。因此,您的应用程序必须能够处理重复消息,它本来就应该能够做到这一点。

可以从源代码中删除或移至策略的镜像经典队列功能

使用仲裁队列时,以下功能将被忽略。处理这些功能的最佳方法是从源代码中删除它们,或将其移至策略。

惰性队列

仲裁队列不支持延迟模式(x-queue-mode=lazy)。

要迁移镜像的延迟经典队列,请删除x-queue-mode=lazy声明参数,或者如果它通过策略设置,则从策略中删除它。有关延迟模式的更多信息,请访问 延迟队列

瞬态队列

瞬态队列 在节点/集群启动时被删除。

计划在未来的 RabbitMQ 版本中删除瞬态队列。届时,瞬态队列的唯一选择将是独占队列。这只会影响队列定义的持久性。消息仍然可以被标记为瞬态。

您必须在迁移之前做出有关瞬态队列的决定:队列的内容是否重要到足以获得仲裁队列的可用性保证,或者是否最好将瞬态队列降级为经典的非镜像队列(经典镜像队列正在被删除,但经典的非镜像队列仍然可用)。

独占队列

您不需要为 独占队列 完成任何迁移任务。独占队列即使在策略指示它们被镜像的情况下也不会被镜像。此外,也不可能创建独占的仲裁队列。

但是,对于独占队列,您必须决定是在迁移过程中将其保留为独占队列,还是将其更改为复制队列。请注意,不要使用显式x-queue-type: quorum 参数创建独占队列声明。

从镜像经典队列迁移到仲裁队列之前的一般先决条件

  1. 具有奇数个节点的 RabbitMQ 集群。RabbitMQ 集群中至少需要 3 个节点才能实现高可用性。
  2. 管理插件应至少在一个节点上运行。它用于导出/导入单个主机的定义,这简化了定义清理。(rabbitmqadmin CLI 命令也在后台使用该插件)。
  3. 要快速移动(铲除)原始队列的积压,请启用 铲除插件 可以用来将原始消息的积压移动到新队列。可以使用 HTTP API 扩展或 RabbitMQ 管理 UI 以编程方式创建铲除。

按虚拟主机将镜像经典队列迁移到仲裁队列

从镜像经典队列迁移到仲裁队列的此过程类似于 蓝绿集群升级,只是您正在迁移到同一 RabbitMQ 集群上的新虚拟主机。以下部分中的步骤使用现有集群上的新虚拟主机,以提供一个空的命名空间,使用旧队列名称创建新的仲裁队列。

您将使用 联邦插件 从旧虚拟主机无缝迁移到新虚拟主机。

重要:您可以为新虚拟主机设置默认队列类型。将其设置为quorum 将创建所有队列,而无需明确的类型作为仲裁队列(独占、非持久或自动删除队列除外)。

如果所有不兼容的功能都已从源代码中清理,并且源代码中没有显式x-queue-type 参数,那么相同的代码应该适用于具有经典镜像队列的旧虚拟主机和具有仲裁队列的新虚拟主机。您需要做的唯一更改是更新虚拟主机连接参数以连接到新虚拟主机。

创建目标虚拟主机

创建新虚拟主机 在现有集群中使用正确的默认队列类型(仲裁)。队列类型应在通过管理 UI 添加新虚拟主机时从队列类型下拉列表中选择。或者,它也可以使用 CLI 接口创建,方法是指定默认队列类型并添加权限。确保所有必需的用户都有权访问并可以连接到新虚拟主机,方法是按照 设置权限 中的步骤操作。

rabbitmqctl add_vhost NEW_VHOST --default-queue-type quorum
rabbitmqctl set_permissions -p NEW_VHOST USERNAME '.*' '.*' '.*'

创建联邦上游

对于 NEW_VHOST 应该创建一个新的 联邦上游,其 URI 指向 OLD_VHOST:amqp:///OLD_VHOST。(请注意,默认的 vhost URI 为amqp:///%2f)。

可以使用管理 UI 或 CLI 创建联邦上游

rabbitmqctl set_parameter federation-upstream quorum-migration-upstream \
--vhost NEW_VHOST \
'{"uri":"amqp:///OLD_VHOST", "trust-user-id":true}'

当使用这种带有空主机名的 URI 形式时,不需要指定凭据。连接只能在单个集群的范围内进行。

如果消息中的user-id 用于任何目的,它也可以像之前的 CLI 示例中所示那样保留。

移动定义

定义 从源虚拟主机导出到文件。这在管理 UI 的概述页面上可用(不要忘记选择单个虚拟主机)。或者,您可以使用以下命令从 CLI 导出定义

rabbitmqadmin export -V OLD_VHOST OLD_VHOST.json

在将此文件加载回 NEW_VHOST 之前,对该文件进行以下更改

  1. 删除您希望在旧虚拟主机中作为经典镜像队列,在新虚拟主机中作为仲裁队列的队列的x-queue-type 声明。
  2. 必须对队列定义应用的其他更改
    • 删除x-max-priority 参数。
    • x-overlow 参数设置为reject-publish-dlx 时更改它。将其更改为reject-publish
    • 删除x-queue-mode 参数。
    • durable 属性更改为true
  3. 更改策略中的以下键
    • 删除所有以ha- 开头的部分:ha-modeha-paramsha-sync-modeha-sync-batch-sizeha-promote-on-shutdownha-promote-on-failure
    • 删除queue-mode
    • overflow 设置为reject-publish-dlx 时更改它。将其更改为reject-publish
  4. 经过上一步之后为空的策略应被删除。
  5. 应将与旧虚拟主机的联邦添加到任何剩余的策略中,指向之前创建的联邦上游:"federation-upstream-set":"quorum-migration-upstream"
  6. 如果没有通配符策略(应用于模式为.* 的队列),则必须创建此策略并将其指向联邦上游。这将确保旧虚拟主机中的每个队列都将被联邦化。
  7. 在迁移期间,应删除对交换机应用联邦规则的策略,以避免重复消息。

现在,修改后的模式可以从管理 UI 加载到新虚拟主机中,或者通过从 CLI 运行以下命令加载:

# Import definitions for a single virtual host using rabbitmqadmin.
# See https://rabbitmq.cn/docs/definitions to learn more.
rabbitmqadmin import -V NEW_VHOST NEW_VHOST.json

将消费者指向在新虚拟主机中使用仲裁队列

迁移队列的消费者现在可以通过更新连接参数以连接到新虚拟主机来访问新队列。联邦链接开始从原始队列拉取消息。

与蓝绿集群一样,在所有消费者都迁移后,您可能还需要 添加铲除以将原始队列的积压更有效地移动到新队列,而不是联邦。有关更多信息,请参阅 清空消息

更新生产者以使用新虚拟主机中的交换机

一旦原始队列为空(或者如果不需要完全的消息排序,则几乎为空),生产者应该停止并重新配置为使用新队列声明和虚拟主机(与消费者相同),然后重新启动。旧虚拟主机中的联邦交换机也应该停止,并且应该在新虚拟主机中添加等效的交换机。原始队列可以在它们为空并且没有消息通过它们时被删除。

在足够的系统负载下,旧虚拟主机中的消息不会被获取。如果消息排序很重要,那么应该按照以下步骤完成排序:停止生产者、将剩余的消息铲除到新虚拟主机,然后在新的虚拟主机上启动消费者。

将剩余的消息铲除到新虚拟主机

对于旧虚拟主机中的每个非空队列,都需要配置一个铲除。例如

rabbitmqctl set_parameter shovel migrate-QUEUE_TO_MIGRATE \
'{"src-protocol": "amqp091", "src-uri": "amqp:///OLD_VHOST", "src-queue": "QUEUE_TO_MIGRATE",
"dest-protocol": "amqp091", "dest-uri": "amqp:///NEW_VHOST", "dest-queue": "QUEUE_TO_MIGRATE"}'

在队列被清空后,铲除可以被删除

rabbitmqctl clear_parameter shovel migrate-QUEUE_TO_MIGRATE

将镜像经典队列就地迁移到仲裁队列

这种迁移方式以牺牲正常运行时间为代价,以便您可以在现有虚拟主机和集群中完成迁移。

对于要迁移的每个队列(或一些队列组),应该可以在迁移期间停止所有消费者和生产者。

准备生产者和消费者

所有不兼容的功能都应该被清理。此外,在声明队列的每个地方,最好使x-queue-type 参数可配置,而无需更改应用程序代码。

迁移步骤

  1. 停止消费者和生产者。
  2. 将消息铲除到新的临时队列。
  3. 删除旧队列。
  4. 创建一个新的仲裁队列,其名称与原始队列相同。
  5. 将临时队列的内容铲除到新的仲裁队列。
  6. 配置消费者使用x-queue-typequorum,然后可以启动它们。
© 2024 RabbitMQ. All rights reserved.