虚拟主机
简介
RabbitMQ 是一个多租户系统:连接、交换机、队列、绑定、用户权限、策略和一些其他事物都属于虚拟主机,实体的逻辑分组。如果您熟悉 Apache 中的虚拟主机或 Nginx 中的服务器块,那么这个概念是相似的。
然而,有一个重要的区别:Apache 中的虚拟主机是在配置文件中定义的;RabbitMQ 的情况并非如此:虚拟主机是使用 rabbitmqctl
或 HTTP API 创建和 删除的。
逻辑和物理分离
虚拟主机提供资源的逻辑分组和隔离。物理资源的分离不是虚拟主机的目标,尽管可以为单个虚拟主机限制某些资源。
例如,RabbitMQ 中的资源权限的作用域是每个虚拟主机。用户没有全局权限,只有在一个或多个虚拟主机中的权限。用户标签可以被认为是全局权限,但它们是规则的例外。
因此,在讨论用户权限时,明确它们适用于哪个(哪些)虚拟主机非常重要。
虚拟主机和客户端连接
虚拟主机有一个名称。当 AMQP 0-9-1 客户端连接到 RabbitMQ 时,它会指定要连接的 vhost 名称。如果身份验证成功,并且提供的用户名被授予了对 vhost 的权限,则连接建立。
与 vhost 的连接只能在该 vhost 中操作交换机、队列、绑定等等。“互连”,例如不同 vhost 中的队列和交换机,只有当应用程序同时连接到两个 vhost 时才有可能。例如,一个应用程序可以从一个 vhost 消费,然后重新发布到另一个 vhost。这种情况可能涉及不同集群或同一集群(或单个节点)中的 vhost。RabbitMQ Shovel 插件是此类应用程序的一个示例。
创建虚拟主机
可以使用 CLI 工具或 HTTP API 端点创建虚拟主机。
新创建的 vhost 将具有一组默认的交换机,但没有其他实体,也没有用户权限。为了让用户能够连接和使用虚拟主机,必须向每个将使用该 vhost 的用户授予对其的权限,例如使用 rabbitmqctl set_permissions。
使用 CLI 工具
可以使用 rabbitmqctl 的 add_vhost
命令创建虚拟主机,该命令只接受虚拟主机名称作为强制参数。
这是一个创建名为 qa1
的虚拟主机的示例
rabbitmqctl add_vhost qa1
使用 HTTP API
可以使用 PUT /api/vhosts/{name}
HTTP API 端点创建虚拟主机,其中 {name}
是虚拟主机的名称
这是一个使用 curl 创建虚拟主机 vh1
的示例,通过连接到 rabbitmq.local:15672
的节点
curl -u userename:pa$sw0rD -X PUT http://rabbitmq.local:15672/api/vhosts/vh1
批量创建和预配置
虚拟主机的创建涉及阻塞的集群范围事务。每个节点都必须执行一些设置步骤,这些步骤的开销适中。实际上,创建一个虚拟主机可能需要几秒钟的时间。
当循环创建大量虚拟主机时,CLI 和 HTTP API 客户端可能会超过虚拟主机的实际创建速率并遇到超时。如果是这种情况,应增加操作超时,并在操作之间引入延迟。
定义导出和导入是在部署时预配置许多虚拟主机的推荐方法。
虚拟主机元数据
虚拟主机可以关联元数据
- 描述
- 一组标签
- 为虚拟主机配置的默认队列类型
所有这些设置都是可选的。它们可以在创建虚拟主机时提供,也可以稍后更新。
使用 CLI 工具
rabbitmqctl add_vhost
命令接受虚拟主机名称以及一些可选标志。
这是一个创建名为 qa1
的虚拟主机的示例,默认队列类型为仲裁队列,并带有描述和两个标签
rabbitmqctl add_vhost qa1 --description "QA env 1" --default-queue-type quorum
rabbitmqctl update_vhost_metadata
可用于更新上面演示的所有或部分元数据值
rabbitmqctl update_vhost_metadata qa1 --description "QA environment for issue 1662" --default-queue-type quorum --tags qa,project-a,qa-1662
要检查虚拟主机元数据,请使用 rabbitmqctl list_vhosts
并提供额外的列
rabbitmqctl -q --formatter=pretty_table list_vhosts name description tags default_queue_type
使用 HTTP API
PUT /api/vhosts/{name}
HTTP API 端点接受一些可选键。
这是一个使用 curl 创建虚拟主机 qa1
的示例,通过连接到 rabbitmq.local:15672
的节点。 仲裁队列将用作默认队列类型,并带有描述和两个标签
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"
默认值仅对新的队列声明有效;更新默认值不会影响任何现有队列或流的队列类型,因为队列类型是不可变的,并且在声明后无法更改。
对于声明时没有显式设置队列类型的队列,有效的虚拟主机默认值将在定义导出时注入到队列属性中。
节点范围默认队列类型(节点范围 DQT)
可以设置节点范围的默认值,而不是为集群中的每个虚拟主机配置相同的默认队列类型,可以使用 rabbitmq.conf
# supported values are: quorum, stream, classic, or a custom queue type module name
default_queue_type = quorum
当同时设置虚拟主机 DQT 和节点范围 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
命令删除虚拟主机,该命令只接受虚拟主机名称作为强制参数。
这是一个删除名为 qa1
的虚拟主机的示例
rabbitmqctl delete_vhost qa1
使用 HTTP API
可以使用 DELETE /api/vhosts/{name}
HTTP API 端点删除虚拟主机,其中 {name}
是虚拟主机的名称。
这是一个使用 curl 删除虚拟主机 vh1
的示例,通过连接到 rabbitmq.local:15672
的节点
curl -u userename:pa$sw0rD -X DELETE http://rabbitmq.local:15672/api/vhosts/vh1
删除保护
可以保护虚拟主机免受删除。受保护的虚拟主机在移除保护之前无法删除。
使用 CLI 工具
rabbitmqctl enable_vhost_protection_from_deletion
是将虚拟主机标记为受保护免受删除的命令
rabbitmqctl enable_vhost_protection_from_deletion "vhost-name"
然后尝试删除虚拟主机将失败,并显示特定消息
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
要移除保护,请使用 rabbitmqctl disable_vhost_protection_from_deletion
## removes virtual host deletion protection
rabbitmqctl disable_vhost_protection_from_deletion "vhost-name"
移除保护后,虚拟主机可以再次删除
rabbitmqctl delete_vhost "vhost-name"
# => Deleting vhost "vhost-name" ...
要查看虚拟主机是否受到删除保护,请使用带有额外列 protected_from_deletion
的 list_vhosts
命令
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 │
# => └───────────────────────────┴─────────────────────────┘
使用 HTTP API
可以使用 POST /api/vhosts/{name}/deletion/protection
HTTP API 端点保护虚拟主机免受删除,其中 {name}
是虚拟主机的名称。
这是一个使用 curl 删除虚拟主机 vh1
的示例,通过连接到 rabbitmq.local:15672
的节点
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 http://localhost: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 POST http://rabbitmq.local:15672/api/vhosts/vh1/deletion/protection
移除保护后,虚拟主机可以再次删除
curl -vv -sL -u guest:guest -X DELETE http://localhost: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 http://localhost: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"
}
限制
在某些情况下,希望限制 vhost 中允许的最大队列数或并发客户端连接数。每个虚拟主机的限制正是为此类情况而存在的。
可以使用 rabbitmqctl
或 HTTP API 配置这些限制。
使用 rabbitmqctl 配置限制
rabbitmqctl set_vhost_limits
是用于定义 vhost 限制的命令。它需要一个 vhost 参数和一个限制定义的 JSON 文档。
配置最大连接数限制
要限制 vhost vhost_name
中的并发客户端连接总数,请使用以下限制定义
rabbitmqctl set_vhost_limits -p vhost_name '{"max-connections": 256}'
要阻止客户端连接到 vhost,请将限制设置为零
rabbitmqctl set_vhost_limits -p vhost_name '{"max-connections": 0}'
要取消限制,请将其设置为负值
rabbitmqctl set_vhost_limits -p vhost_name '{"max-connections": -1}'
配置最大队列数
要限制 vhost vhost_name
中的队列总数,请使用以下限制定义
rabbitmqctl set_vhost_limits -p vhost_name '{"max-queues": 1024}'
要取消限制,请将其设置为负值
rabbitmqctl set_vhost_limits -p vhost_name '{"max-queues": -1}'
虚拟主机和 STOMP
与 AMQP 0-9-1 一样,STOMP 包括虚拟主机的概念。有关详细信息,请参阅 STOMP 指南。
虚拟主机和 MQTT
与 AMQP 0-9-1 和 STOMP 不同,MQTT 没有虚拟主机的概念。默认情况下,MQTT 连接使用单个 RabbitMQ 主机。有一些 MQTT 特定的约定和功能,使客户端无需任何客户端库修改即可连接到特定的 vhost。有关详细信息,请参阅 MQTT 指南。