RabbitMQ URI 规范
概述
本规范定义了一种 "amqp" URI 方案。符合规范的 URI 表示 AMQP 0-9-1 客户端以及某些 RabbitMQ 插件连接到 RabbitMQ 节点所需的信息。
简介
本规范的范围仅限于 AMQP 0-9-1,这是 RabbitMQ 实现的原始协议。AMQP 0-9-1 客户端连接到 RabbitMQ 节点,以便根据消息传递模型发布和消费消息。
客户端建立和协商 AMQP 0-9-1 连接需要多项信息。这些连接参数包括:
建立到底层服务器 TCP/IP 连接所需的参数(即主机地址和端口)。
客户端认证信息。AMQP 0-9-1 使用 SASL 进行认证。通常使用
PLAIN机制,因此认证参数由用户名和密码组成。“虚拟主机”(或 vhost)的名称,它为协议引用的实体(如交换机和队列)指定命名空间。注意,这并非 HTTP 意义上的虚拟主机。
RabbitMQ 客户端通常会从配置文件或环境变量中获取所有这些参数来建立连接。因此,如果能将连接参数组合成单个字符串,而不是作为不同的配置设置,会更加方便。这意味着只需要一个配置设置,并且只需要将一个值传递给客户端库即可。
但是,将连接参数组合成单个字符串需要一种客户端库能理解的约定,即明确如何表示和分隔这些连接参数。标准化该约定是理想的做法,以便许多 AMQP 0-9-1 客户端库能一致地实现它。这种标准的一个显而易见的依据是 RFC3986 中定义的通用 URI 语法。
本规范的目的是定义 "amqp" 和 "amqps" URI 方案,在通用 URI 语法内表示 AMQP 0-9-1 连接参数。
"amqp" URI 方案
AMQP 0-9-1 URI 的语法由以下 ABNF 规则定义。这些规则中未定义的所有名称均取自 RFC3986。
amqp_URI = "amqp://" amqp_authority [ "/" vhost ] [ "?" query ]
amqp_authority = [ amqp_userinfo "@" ] host [ ":" port ]
amqp_userinfo = username [ ":" password ]
username = *( unreserved / pct-encoded / sub-delims )
password = *( unreserved / pct-encoded / sub-delims )
vhost = segment
一旦 URI 根据此语法成功解析,连接参数将按以下章节所述进行确定。
主机 (Host)
建立底层 TCP 连接的主机由 RFC3986 第 3.2.2 节定义的 host 组件确定。请注意,根据 ABNF,host 组件不能缺失,但长度可以为零。
端口 (Port)
建立底层 TCP 连接的端口号由 RFC3986 定义的 port 组件确定。port 组件可以缺失,表现为缺少将其与主机分隔的 ":" 字符。如果缺失,则应替换为 IANA 分配的 AMQP 0-9-1 端口号 5672。
用户名和密码
如果存在,用户名和密码组件应在通过 connection.secure 和 connection.secure-ok AMQP 0-9-1 方法发生的 SASL 交换中使用。用户名和密码中的任何百分号编码八位字节在用于 SASL 交换之前应进行解码,产生的八位字节序列应被视为 UTF-8 编码。
用户名和密码都可以缺失;它们的缺失通过缺少将 amqp_userinfo 与主机分隔的 "@" 字符来表示。如果用户名存在,密码可能缺失;这通过缺少将密码与用户名分隔的 ":" 字符来表示。零长度的用户名和密码不等同于缺失的用户名和密码。
RFC3986 指出,“出现在 userinfo 组件中的密码已被弃用,应视为错误”(第 7.5 节)。虽然在面向用户的应用程序(例如 Web 浏览器)以及可能以不安全方式存储和显示的 URI 环境中,这是一个合理的建议,但对于后端应用程序而言,它并不一定适用。许多此类应用程序是“无头”服务,它们代表整个应用程序而不是特定用户打开 RabbitMQ 连接。因此,用户名和密码标识的是应用程序而非人类用户,并且很可能包含在安全配置存储中的连接参数内。代表特定用户建立 RabbitMQ 连接的面向用户应用程序也是可能的。在这种情况下,用户名和密码可能由用户提供以进行身份验证。但这类应用程序是例外而非规则。因此,实现本规范的应用程序作者不应认为自己受 RFC3986 第 7.5 节的约束。另请参阅下文关于“安全考量”的章节。
虚拟主机 (Virtual Host)
虚拟主机 (vhost) 组件用作 connection.open AMQP 0-9-1 方法的 virtual-host 字段的基础。vhost 中的任何百分号编码八位字节在传递给服务器之前应进行解码。
请注意
- URI 的 vhost 组件不包括路径中开头的 "/" 字符。这使得引用任何 vhost 成为可能,而不仅仅是那些以 "/" 字符开头的 vhost。
- vhost 是单个段。因此,出现在 vhost 名称中的任何 "/" 字符都必须进行百分号编码。具有多段路径的 URI 不符合本规范。
vhost 组件可以缺失;这通过 amqp_authority 后缺少 "/" 字符来表示。缺失的 vhost 组件不等同于空的(即零长度的)vhost 名称。
缺失组件的处理
某些 URI 组件(端口、用户名、密码、vhost 和查询)在 URI 中可能会缺失。主机不能缺失,但长度可以为零;就本节而言,零长度主机被视为缺失。
除了端口(已在上述第 2.2 节中涵盖)之外,本规范不对实现如何处理缺失组件做出强制规定。可能的方法包括但不限于以下几种:
- 可以用默认值替换缺失的组件。
- 面向用户的应用程序可以提示用户提供缺失组件的值。
- 缺失的组件可能导致错误。
此外,应用程序可以针对不同的组件采取不同的策略。
例如,URI "amqp://"(其中所有组件均缺失)可能导致客户端库使用一组默认值,这些默认值对应于连接到本地 RabbitMQ 服务器并以 guest 用户身份进行认证。这对于开发目的来说很方便。
"amqps" URI 方案
"amqps" URI 方案用于指示客户端与服务器建立安全连接。
AMQP 0-9-1 规范假定底层传输层提供可靠的、面向字节流的虚拟电路。当不需要保护网络上的流量时,通常使用 TCP/IP 连接。
在必须保护流量的情况下,可以使用 TLS(参见 RFC5246)。目前的做法是将 AMQP 0-9-1 简单地分层在 TLS 之上以形成 "AMQPS"(类似于 HTTPS 将 HTTP 分层在 TLS 之上的方式)。AMQP 0-9-1 没有提供将非安全连接升级为安全连接的方法。因此,同时支持安全和非安全连接的服务器必须为这两类连接监听不同的端口。
除方案标识符外,"amqps" URI 方案的语法与 "amqp" URI 方案的语法相同。
amqps_URI = "amqps://" amqp_authority [ "/" vhost ]
amqps URI 的解释在两个方面与对应的 "普通" URI 不同。在所有其他方面,解释方式相同。
- 客户端必须充当 TLS 客户端,并在底层 TCP/IP 连接建立后立即开始 TLS 握手。所有 AMQP 0-9-1 协议数据均作为 TLS “应用程序数据”发送。除此以外,遵循正常的 AMQP 0-9-1 行为。
- 如果 URI 中缺少端口号,则应使用 IANA 为 "amqps" 分配的端口号 5671。
安全考量
如上述第 2.3 节所述,URI 通常作为配置设置提供给应用程序。在这种情况下,如果密码不能合并到 URI 中,它将简单地作为单独的配置设置提供。这降低了使用 URI 的好处,且没有提高安全性。因此,本规范覆盖了 RFC3986 对 userinfo 组件内密码的弃用。
开发者在不影响安全性的情况下可以自由使用密码组件。尽管如此,他们应该意识到密码组件的内容可能是敏感的,并应避免泄漏(例如,完整的 URI 不应出现在异常消息或日志记录中,这些信息可能会被权限较低的人员查看)。
附录 A:示例
下面是一个示例表,展示了如何根据本规范解析 URI。许多示例旨在展示边缘情况,以阐明规范并为解析 URI 的代码提供测试用例。每一行显示一个 URI 以及每个组件产生的八位字节序列。这些八位字节序列用双引号括起来。空单元格表示缺失的组件,如第 3 节所述。
| URI | 用户名 | 密码 | 主机 (Host) | 端口 | 虚拟主机 (Vhost) |
|---|---|---|---|---|---|
| amqp://user:pass@host:10000/vhost | "user" | "pass" | "host" | 10000 | "vhost" |
| amqp://user:passw%23rd@host:10000/vhost | "user" | "passw#rd" | "host" | 10000 | "vhost" |
| amqp://user%61:%61pass@ho%61st:10000/v%2fhost | "usera" | "apass" | "hoast" | 10000 | "v/host" |
| amqp:// | |||||
| amqp://:@/ | "" | "" | "" | ||
| amqp://user@ | "user" | ||||
| amqp://user:pass@ | "user" | "pass" | |||
| amqp://host | "host" | ||||
| amqp://:10000 | 10000 | ||||
| amqp:///vhost | "vhost" | ||||
| amqp://host/ | "host" | "" | |||
| amqp://host/%2f | "host" | "/" | |||
| amqp://[::1] | "[::1]" (即 IPv6 地址 ::1) |
附录 B:查询参数
客户端可能需要进一步参数化来定义它们应如何连接到服务器。标准的 URI 查询语法可用于为客户端提供额外信息。
查询参数可能比其他 URI 部分更具实现相关性;因此,本文档不会尝试规定它们应如何使用。但是,我们已经记录了官方支持的客户端如何读取 URI 查询参数。