虚拟主机
简介
RabbitMQ 是一个多租户系统:连接、交换机、队列、绑定、用户权限、策略以及其他一些内容都属于虚拟主机(virtual hosts),即实体的逻辑分组。如果您熟悉 Apache 中的虚拟主机 或 Nginx 中的服务器块,那么这个概念是类似的。
然而,有一个重要的区别:Apache 中的虚拟主机是在配置文件中定义的;而 RabbitMQ 则不然:虚拟主机是使用 rabbitmqctl 或 HTTP API 进行创建和删除的。
逻辑与物理隔离
虚拟主机提供了资源的逻辑分组和隔离。尽管可以对单个虚拟主机限制某些资源,但物理资源的隔离并不是虚拟主机的目标。
例如,RabbitMQ 中的资源权限是以虚拟主机为作用域的。用户没有全局权限,只有在一个或多个虚拟主机中的权限。用户标签(User tags)可以被视为全局权限,但它们是该规则的例外。
因此,在讨论用户权限时,明确它们适用于哪些虚拟主机非常重要。
虚拟主机与客户端连接
虚拟主机有一个名称。当 AMQP 0-9-1 客户端连接到 RabbitMQ 时,它会指定要连接的 vhost 名称。如果身份验证成功,并且所提供的用户名被授予了该 vhost 的权限,则建立连接。
与 vhost 的连接只能对该 vhost 中的交换机、队列、绑定等进行操作。例如,只有当应用程序同时连接到两个 vhost 时,才能实现不同 vhost 中队列和交换机的“互联”。例如,应用程序可以从一个 vhost 消费消息,然后重新发布到另一个 vhost。这种情况可能涉及不同集群中的 vhost,也可能涉及同一个集群(或单个节点)中的 vhost。RabbitMQ Shovel 插件就是此类应用程序的一个例子。
创建虚拟主机
可以使用 CLI 工具或 HTTP API 端点来创建虚拟主机。
新创建的 vhost 将包含一组默认的交换机,但没有其他实体和任何用户权限。为了让用户能够连接并使用该虚拟主机,必须为每个将要使用该 vhost 的用户授予权限,例如使用 rabbitmqctl set_permissions。
使用 CLI 工具
可以使用 rabbitmqctl 的 add_vhost 命令创建虚拟主机,该命令接受虚拟主机名称作为唯一的必需参数。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl add_vhost qa1
rabbitmqadmin vhosts declare --name qa1
rabbitmqctl.bat add_vhost qa1
rabbitmqadmin.exe vhosts declare --name qa1
使用 HTTP API
可以使用 PUT /api/vhosts/{name} HTTP API 端点创建虚拟主机,其中 {name} 是虚拟主机的名称。
以下是一个使用 curl 通过连接到 rabbitmq.local:15672 处的节点来创建虚拟主机 vh1 的示例。
curl -u userename:pa$sw0rD -X PUT http://rabbitmq.local:15672/api/vhosts/vh1
批量创建与预配置
虚拟主机的创建涉及集群范围的阻塞事务。每个节点都必须执行若干个设置步骤,这些步骤的开销适中。在实践中,创建一个虚拟主机可能需要几秒钟的时间。
当在循环中创建大量虚拟主机时,CLI 和 HTTP API 客户端可能会超过虚拟主机的实际创建速度并遇到超时。如果出现这种情况,应增加操作超时时间,并在操作之间引入延迟。
定义导出与导入是在部署时预配置多个虚拟主机的推荐方法。
虚拟主机元数据
虚拟主机可以关联以下元数据:
- 描述(Description)
- 一组标签(Tags)
- 为虚拟主机配置的默认队列类型
所有这些设置都是可选的。它们可以在创建虚拟主机时提供,也可以稍后更新。
使用 CLI 工具
虚拟主机元数据可以在创建时设置,也可以稍后更新。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
# Create a virtual host with metadata
rabbitmqctl add_vhost qa1 --description "QA env 1" --default-queue-type quorum --tags qa,project-a
# Update virtual host metadata
rabbitmqctl update_vhost_metadata qa1 --description "QA environment for issue 1662" --default-queue-type quorum --tags qa,project-a,qa-1662
# List virtual hosts with metadata
rabbitmqctl -q --formatter=pretty_table list_vhosts name description tags default_queue_type
# Create a virtual host with metadata
rabbitmqadmin vhosts declare --name qa1 \
--description "QA environment 1" \
--default-queue-type quorum
# Create a virtual host with tracing enabled
rabbitmqadmin vhosts declare --name qa-tracing \
--description "QA environment with tracing" \
--default-queue-type quorum \
--tracing
rabbitmqadmin vhosts list
# Create a virtual host with metadata
rabbitmqctl.bat add_vhost qa1 --description "QA env 1" --default-queue-type quorum
# Update virtual host metadata
rabbitmqctl.bat update_vhost_metadata qa1 --description "QA environment for issue 1662" --default-queue-type quorum --tags qa,project-a,qa-1662
# List virtual hosts with metadata
rabbitmqctl.bat -q --formatter=pretty_table list_vhosts name description tags default_queue_type
# Create a virtual host with metadata
rabbitmqadmin.exe vhosts declare --name qa1 ^
--description "QA environment 1" ^
--default-queue-type quorum
# Create a virtual host with tracing enabled
rabbitmqadmin.exe vhosts declare --name qa-tracing ^
--description "QA environment with tracing" ^
--default-queue-type quorum ^
--tracing
rabbitmqadmin.exe vhosts list
使用 HTTP API
PUT /api/vhosts/{name} HTTP API 端点接受多个可选键。
以下是一个使用 curl 通过连接到 rabbitmq.local:15672 处的节点来创建虚拟主机 qa1 的示例。仲裁队列(Quorum queues)将作为默认队列类型,同时包含一个描述和两个标签。
curl -u userename:pa$sw0rD -X PUT http://rabbitmq.local:15672/api/vhosts/qa1 \
-H "content-type: application/json" \
--data-raw '{"description": "QA environment 1", "tags": "qa,project-a", "default_queue_type": "quorum"}'
相同的端点可用于更新上述演示的部分或全部元数据值。
curl -u userename:pa$sw0rD -X PUT http://rabbitmq.local:15672/api/vhosts/qa1 \
-H "content-type: application/json" \
--data-raw '{"description": "QA environment for issue 1662", "tags": "qa,project-a,qa-1662", "default_queue_type": "quorum"}'
虚拟主机元数据由 GET /api/vhosts/{name} 端点返回。
curl -u userename:pa$sw0rD -X GET http://rabbitmq.local:15672/api/vhosts/qa1
默认队列类型 (DQT)
当客户端声明队列而未通过 x-queue-type 标头显式指定其类型时,将使用可配置的默认类型。该默认值可以通过在虚拟主机元数据中指定来覆盖(见上文)。
rabbitmqctl add_vhost qa1 --description "QA environment 1" --default-queue-type quorum --tags qa,project-a
支持的队列类型包括:
- “quorum”(仲裁队列)
- “stream”(流)
- “classic”(经典队列)
该默认值仅对新的队列声明有效;更新默认值不会影响任何现有队列或流的类型,因为队列类型是不可变的,一旦声明后无法更改。
对于在未显式设置队列类型的情况下声明的队列,在导出定义时,有效的虚拟主机默认值将被注入到队列属性中。
节点范围的默认队列类型 (Node-wide DQT)
与其为集群中的每个虚拟主机配置相同的默认队列类型,不如使用 rabbitmq.conf 设置节点范围的默认值。
# supported values are: quorum, stream, classic, or a custom queue type module name
default_queue_type = quorum
当同时设置了虚拟主机 DQT 和节点范围的 DQT 时,虚拟主机级别的设置具有优先权。
迁移到仲裁队列:一种放宽队列属性等价性检查的方法
队列属性等价性检查中的队列类型检查可以通过布尔设置 quorum_queue.property_equivalence.relaxed_checks_on_redeclaration 来放宽,这使得为仲裁队列放宽队列属性等价性检查成为可能。
具体而言,当仲裁队列被重新声明且客户端提供的类型设置为“classic”时,此设置将有助于避免通道异常,从而更容易逐步迁移到仲裁队列,而无需在短时间内升级所有应用程序。
# this setting is meant to be used during transitionary periods when
# RabbitMQ default queue type is changed but not all applications have been
# updated yet
quorum_queue.property_equivalence.relaxed_checks_on_redeclaration = true
删除虚拟主机
可以使用 CLI 工具或 HTTP API 端点删除虚拟主机。
删除虚拟主机将永久删除其中的所有实体(队列、交换机、绑定、策略、权限等)。
使用 CLI 工具
可以使用 rabbitmqctl 的 delete_vhost 命令删除虚拟主机,该命令接受虚拟主机名称作为唯一的必需参数。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl delete_vhost qa1
rabbitmqadmin vhosts delete --name qa1
rabbitmqctl.bat delete_vhost qa1
rabbitmqadmin.exe vhosts delete --name qa1
使用 HTTP API
可以使用 DELETE /api/vhosts/{name} HTTP API 端点删除虚拟主机,其中 {name} 是虚拟主机的名称。
以下是一个使用 curl 通过连接到 rabbitmq.local:15672 处的节点来删除虚拟主机 vh1 的示例。
curl -u userename:pa$sw0rD -X DELETE http://rabbitmq.local:15672/api/vhosts/vh1
删除多个虚拟主机
rabbitmqadmin 支持使用名称匹配模式一次性删除多个虚拟主机。
这是一个极具破坏性的操作,必须非常谨慎地使用。
此命令将删除所有符合提供的正则表达式模式的虚拟主机。这些虚拟主机中的所有数据都将永久丢失,包括:
- 队列、流和分区流
- 交换器和绑定
- 消息
- 用户权限
- 联合上游(Federation upstreams)和链接
- Shovels
- 策略和操作员策略
- 运行时参数
在执行实际删除之前,请务必先使用 --dry-run 来验证将要删除的内容。
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqadmin (PowerShell)
# ALWAYS run with --dry-run first to see what would be deleted
rabbitmqadmin vhosts delete_multiple --name-pattern "^test-.*" --dry-run
# After verifying with --dry-run, use --approve to perform the actual deletion
# This will delete ALL virtual hosts whose names start with "test-"
rabbitmqadmin vhosts delete_multiple --name-pattern "^test-.\*" --approve
# ALWAYS run with --dry-run first to see what would be deleted
rabbitmqadmin.exe vhosts delete_multiple --name-pattern "^test-.*" --dry-run
# After verifying with --dry-run, use --approve to perform the actual deletion
# This will delete ALL virtual hosts whose names start with "test-"
rabbitmqadmin.exe vhosts delete_multiple --name-pattern "^test-.*" --approve
默认虚拟主机 (/) 始终保留,并且永远不会被此命令删除,即使它匹配该模式。
删除保护
虚拟主机可以免受删除操作的影响。受保护的虚拟主机在删除保护被移除之前无法被删除。
使用 CLI 工具
可以使用 rabbitmqctl 或 rabbitmqadmin 对虚拟主机进行删除保护。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl enable_vhost_protection_from_deletion "vhost-name"
rabbitmqadmin vhosts enable_deletion_protection --name "vhost-name"
rabbitmqctl.bat enable_vhost_protection_from_deletion "vhost-name"
rabbitmqadmin.exe vhosts enable_deletion_protection --name "vhost-name"
尝试删除受保护的虚拟主机将失败并返回特定消息。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl delete_vhost "vhost-name"
# ...
# => Error:
# => Cannot delete this virtual host: it is protected from deletion. To lift the protection, inspect and update its metadata
rabbitmqadmin vhosts delete --name "vhost-name"
# => Error: HTTP request failed with status 412: Precondition Failed
# => Refusing to delete virtual host 'vhost-name' because it is protected from deletion
rabbitmqctl.bat delete_vhost "vhost-name"
# ...
# => Error:
# => Cannot delete this virtual host: it is protected from deletion. To lift the protection, inspect and update its metadata
rabbitmqadmin.exe vhosts delete --name "vhost-name"
# => Error: HTTP request failed with status 412: Precondition Failed
# => Refusing to delete virtual host 'vhost-name' because it is protected from deletion
要移除保护:
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl disable_vhost_protection_from_deletion "vhost-name"
rabbitmqadmin vhosts disable_deletion_protection --name "vhost-name"
rabbitmqctl.bat disable_vhost_protection_from_deletion "vhost-name"
rabbitmqadmin.exe vhosts disable_deletion_protection --name "vhost-name"
移除保护后,虚拟主机即可再次被删除。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl delete_vhost "vhost-name"
# => Deleting vhost "vhost-name" ...
rabbitmqadmin vhosts delete --name "vhost-name"
# => (No output on success)
rabbitmqctl.bat delete_vhost "vhost-name"
# => Deleting vhost "vhost-name" ...
rabbitmqadmin.exe vhosts delete --name "vhost-name"
# => (No output on success)
要查看虚拟主机是否受到删除保护,请使用带有额外列 protected_from_deletion 的 list_vhosts 命令。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl list_vhosts name tags default_queue_type metadata protected_from_deletion --formatter=pretty_table
# => Listing vhosts ...
# => ┌───────────────────────────┬─────────────────────────┐
# => │ name │ protected_from_deletion │
# => ├───────────────────────────┼─────────────────────────┤
# => │ / │ false │
# => ├───────────────────────────┼─────────────────────────┤
# => │ vh1 │ true │
# => ├───────────────────────────┼─────────────────────────┤
# => │ vh2 │ false │
# => └───────────────────────────┴─────────────────────────┘
rabbitmqadmin vhosts list
# Shows all virtual hosts (protection status not displayed in list view)
rabbitmqctl.bat list_vhosts name tags default_queue_type metadata protected_from_deletion --formatter=pretty_table
# => Listing vhosts ...
# => ┌───────────────────────────┬─────────────────────────┐
# => │ name │ protected_from_deletion │
# => ├───────────────────────────┼─────────────────────────┤
# => │ / │ false │
# => ├───────────────────────────┼─────────────────────────┤
# => │ vh1 │ true │
# => ├───────────────────────────┼─────────────────────────┤
# => │ vh2 │ false │
# => └───────────────────────────┴─────────────────────────┘
rabbitmqadmin.exe vhosts list
# Shows all virtual hosts (protection status not displayed in list view)
使用 HTTP API
可以使用 POST /api/vhosts/{name}/deletion/protection HTTP API 端点保护虚拟主机免遭删除,其中 {name} 是虚拟主机的名称。
以下是一个使用 curl 通过连接到 rabbitmq.local:15672 处的节点来保护虚拟主机 vh1 免遭删除的示例。
curl -u userename:pa$sw0rD -X POST http://rabbitmq.local:15672/api/vhosts/vh1/deletion/protection
此时,任何尝试删除该虚拟主机的行为都将失败,并返回 412 Precondition Failed 状态。
curl -sL -u guest:guest -X DELETE https://:15672/api/vhosts/vh1/
# => < HTTP/1.1 412 Precondition Failed
响应正文将包含一条特定错误,类似于 CLI 工具输出的内容。
{
"error": "precondition_failed",
"reason": "Refusing to delete virtual host 'vh1' because it is protected from deletion"
}
要移除保护,请使用 DELETE /api/vhosts/{name}/deletion/protection。
curl -u userename:pa$sw0rD -X DELETE http://rabbitmq.local:15672/api/vhosts/vh1/deletion/protection
移除保护后,虚拟主机即可再次被删除。
curl -vv -sL -u guest:guest -X DELETE https://:15672/api/vhosts/
# ...
# => < HTTP/1.1 204 No Content
要查看虚拟主机是否受到删除保护,请使用 GET /api/vhosts 或 GET /api/vhosts/{vhost} 端点,然后检查响应正文中的 metadata.protected_from_deletion 字段。
curl -sL -u guest:guest -X GET https://:15672/api/vhosts/vh1
# => {
# => "name": "vh1",
# => "description": "",
# => "tags": [],
# => "default_queue_type": "classic",
# => "protected_from_deletion": true,
# => "metadata": {
# => "description": "",
# => "tags": [],
# => "default_queue_type": "classic",
# => "protected_from_deletion": true
# => },
# => "tracing": false,
# => "cluster_state": {
# => "rabbit@sunnyside": "running"
# => }
# => }
定义导入
如果通过定义文件创建虚拟主机,添加一个新的元数据键 "protected_from_deletion" 并将其设置为 true,则在创建时会将该虚拟主机标记为受保护。
{
"name": "protected",
"description": "",
"metadata": {
"description": "This virtual host is protected from deletion with a special metadata key",
"tags": [],
"default_queue_type": "classic",
"protected_from_deletion": true
},
"tags": [],
"default_queue_type": "classic"
}
限制
在某些情况下,限制虚拟主机中允许的最大队列数或并发客户端连接数是很有必要的。针对虚拟主机的限制正是为此类场景而设计的。
这些限制可以使用 rabbitmqctl 或 HTTP API 进行配置。
配置限制
虚拟主机限制可以使用 rabbitmqctl 或 rabbitmqadmin 进行配置。
配置最大连接限制
要限制虚拟主机 vhost_name 中的并发客户端连接总数,请使用以下限制定义:
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl set_vhost_limits -p vhost_name '{"max-connections": 256}'
rabbitmqadmin --vhost vhost_name vhost_limits declare --name max-connections --value 256
rabbitmqctl.bat set_vhost_limits -p vhost_name "{""max-connections"": 256}"
rabbitmqadmin.exe --vhost vhost_name vhost_limits declare --name max-connections --value 256
要阻止对 vhost 的客户端连接,请将限制设置为零。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl set_vhost_limits -p vhost_name '{"max-connections": 0}'
rabbitmqadmin --vhost vhost_name vhost_limits declare --name max-connections --value 0
rabbitmqctl.bat set_vhost_limits -p vhost_name "{""max-connections"": 0}"
rabbitmqadmin.exe --vhost vhost_name vhost_limits declare --name max-connections --value 0
配置最大队列数
要限制 vhost vhost_name 中的队列总数,请使用以下限制定义:
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl set_vhost_limits -p vhost_name '{"max-queues": 1024}'
rabbitmqadmin --vhost vhost_name vhost_limits declare --name max-queues --value 1024
rabbitmqctl.bat set_vhost_limits -p vhost_name "{""max-queues"": 1024}"
rabbitmqadmin.exe --vhost vhost_name vhost_limits declare --name max-queues --value 1024
列出虚拟主机限制
要列出虚拟主机的限制:
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
# uses the default virtual host (/)
rabbitmqctl list_vhost_limits
# or for a specific virtual host
rabbitmqctl list_vhost_limits -p vhost_name
# uses the default virtual host (/)
rabbitmqadmin vhost_limits list
# or for a specific virtual host
rabbitmqadmin --vhost vhost_name vhost_limits list
# uses the default virtual host (/)
rabbitmqctl.bat list_vhost_limits
# or for a specific virtual host
rabbitmqctl.bat list_vhost_limits -p vhost_name
# uses the default virtual host (/)
rabbitmqadmin.exe vhost_limits list
# or for a specific virtual host
rabbitmqadmin.exe --vhost vhost_name vhost_limits list
清除虚拟主机限制
要清除虚拟主机限制:
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl clear_vhost_limits -p vhost_name '{"max-connections": 256}'
rabbitmqadmin --vhost vhost_name vhost_limits delete --name max-connections
rabbitmqctl.bat clear_vhost_limits -p vhost_name "{""max-connections"": 256}"
rabbitmqadmin.exe --vhost vhost_name vhost_limits delete --name max-connections
预配置虚拟主机限制
虚拟主机限制可以在 rabbitmq.conf 中针对一组虚拟主机进行预配置。当虚拟主机由集群用户动态创建(例如,将 RabbitMQ 集群作为服务提供)但所有新创建的虚拟主机都必须使用一致的限制设置时,这非常有用。
可以定义多组限制,每一组都包含一个匹配虚拟主机名称的模式和一组要设置的限制。
# Default limits for virtual hosts starting with "pipelines"
default_limits.vhosts.1.pattern = ^pipelines
default_limits.vhosts.1.max_connections = 10
default_limits.vhosts.1.max_queues = 1000
# Default limits for virtual hosts starting with "telemetry"
default_limits.vhosts.2.pattern = ^telemetry
default_limits.vhosts.2.max_connections = 10000
default_limits.vhosts.2.max_queues = 10000
# Default limits for all other virtual hosts
default_limits.vhosts.3.pattern = .*
default_limits.vhosts.3.max_connections = 20
default_limits.vhosts.3.max_queues = 20
使用 -1 表示特定限制不受限制。
虚拟主机与 STOMP
与 AMQP 0-9-1 一样,STOMP 包含了虚拟主机的概念。详情请参阅 STOMP 指南。
虚拟主机与 MQTT
与 AMQP 0-9-1 和 STOMP 不同,MQTT 没有虚拟主机的概念。MQTT 连接默认使用单个 RabbitMQ 主机。存在一些 MQTT 特有的约定和功能,使得客户端无需修改任何客户端库即可连接到特定的 vhost。详情请参阅 MQTT 指南。