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 已根据此语法成功解析,连接参数将按照以下各节所述确定。
主机
建立底层 TCP 连接的主机是从主机组件根据 RFC3986 第 3.2.2 节确定的。请注意,根据 ABNF,主机组件可能不存在,但它可以是零长度的。
端口
建立底层 TCP 连接的端口号是从端口组件根据 RFC3986 确定的。端口组件可能不存在,表示为主机和端口之间缺少“:”字符。如果不存在,则应替换为 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 节的约束。另请参阅下面的“安全注意事项”部分。
虚拟主机
虚拟主机(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 | 用户名 | 密码 | 主机 | 端口 | 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 查询参数。