Azure 服务总线和事件中心内的 AMQP 1.0 协议指南AMQP 1.0 in Azure Service Bus and Event Hubs protocol guide

高级消息队列协议 1.0 是一种标准化组帧和传输协议,能够以异步、安全且可靠的方式在两方之间传输消息。The Advanced Message Queueing Protocol 1.0 is a standardized framing and transfer protocol for asynchronously, securely, and reliably transferring messages between two parties. 它是 Azure 服务总线消息传送和 Azure 事件中心的主要协议。It is the primary protocol of Azure Service Bus Messaging and Azure Event Hubs. 这两种服务还支持 HTTPS。Both services also support HTTPS. 同时支持的专用 SBMP 协议即将淘汰并由 AMQP 取代。The proprietary SBMP protocol that is also supported is being phased out in favor of AMQP.

AMQP 1.0 是中间件供应商(例如 Microsoft 和 Red Hat)与许多消息传送中间件用户(例如代表金融服务行业的 JP Morgan Chase)广泛合作的成果。AMQP 1.0 is the result of broad industry collaboration that brought together middleware vendors, such as Microsoft and Red Hat, with many messaging middleware users such as JP Morgan Chase representing the financial services industry. OASIS 是 AMQP 协议和扩展规范的技术标准化论坛,它已获 ISO/IEC 19494 国际标准的官方批准。The technical standardization forum for the AMQP protocol and extension specifications is OASIS, and it has achieved formal approval as an international standard as ISO/IEC 19494.

目标Goals

本文简要说明 AMQP 1.0 消息传送规范的核心概念以及当前正由 OASIS AMQP 技术委员定案的一小部分草稿扩展规范,并说明 Azure 服务总线如何根据这些规范进行实现和构建。This article briefly summarizes the core concepts of the AMQP 1.0 messaging specification along with a small set of draft extension specifications that are currently being finalized in the OASIS AMQP technical committee and explains how Azure Service Bus implements and builds on these specifications.

目的是要让在任何平台上使用任何现有 AMQP 1.0 客户端堆栈的开发人员通过 AMQP 1.0 与 Azure 服务总线交互。The goal is for any developer using any existing AMQP 1.0 client stack on any platform to be able to interact with Azure Service Bus via AMQP 1.0.

常见的通用型 AMQP 1.0 堆栈(例如 Apache Proton 或 AMQP.NET Lite)已实现所有核心 AMQP 1.0 协议。Common general-purpose AMQP 1.0 stacks, such as Apache Proton or AMQP.NET Lite, already implement all core AMQP 1.0 protocols. 这些基本手势有时以更高级别的 API 包装;Apache Proton 甚至提供两个 API:命令式 Messenger API 和反应式 Reactor API。Those foundational gestures are sometimes wrapped with a higher-level API; Apache Proton even offers two, the imperative Messenger API and the reactive Reactor API.

在以下介绍中,我们假设 AMQP 连接、会话和链接的管理以及帧传输和流量控制的处理都由相应的堆栈(例如 Apache Proton-C)处理,而不需要应用程序开发人员特别注意。In the following discussion, we assume that the management of AMQP connections, sessions, and links and the handling of frame transfers and flow control are handled by the respective stack (such as Apache Proton-C) and do not require much if any specific attention from application developers. 抽象假设存在一些 API 基元(例如连接能力)以及创建某种形式的 sender 和 receiver 抽象对象的能力,并分别具有 send()receive() 的某种形式的操作 。We abstractly assume the existence of a few API primitives like the ability to connect, and to create some form of sender and receiver abstraction objects, which then have some shape of send() and receive() operations, respectively.

介绍 Azure 服务总线的高级功能(例如消息浏览或会话管理)时,以 AMQP 术语说明这些功能,但也可作为一种分层虚拟实现,基于此假设的 API 抽象。When discussing advanced capabilities of Azure Service Bus, such as message browsing or management of sessions, those features are explained in AMQP terms, but also as a layered pseudo-implementation on top of this assumed API abstraction.

AMQP 是什么?What is AMQP?

AMQP 是一种组帧和传输协议。AMQP is a framing and transfer protocol. 组帧表示它为以网络连接的任一方向流入的二进制数据流提供结构。Framing means that it provides structure for binary data streams that flow in either direction of a network connection. 该结构针对要在已连接方之间交换的不同数据块(称为“帧” )提供略图。The structure provides delineation for distinct blocks of data, called frames, to be exchanged between the connected parties. 传输功能确保通信双方都可以对于何时应发送帧以及传输何时应视为完成建立共识。The transfer capabilities make sure that both communicating parties can establish a shared understanding about when frames shall be transferred, and when transfers shall be considered complete.

与 AMQP 工作组生成且前面过期的草稿版本(仍有一些消息代理在使用)不同,任务组的最终标准化 AMQP 1.0 协议并未规定要存在消息代理或消息中转站内实体的任何特定拓扑。Unlike earlier expired draft versions produced by the AMQP working group that are still in use by a few message brokers, the working group's final, and standardized AMQP 1.0 protocol does not prescribe the presence of a message broker or any particular topology for entities inside a message broker.

该协议可用于对称的对等通信,以便与支持队列和发布/订阅实体的消息代理交互,如 Azure 服务总线所为。The protocol can be used for symmetric peer-to-peer communication, for interaction with message brokers that support queues and publish/subscribe entities, as Azure Service Bus does. 它还可以用于与消息传送基础结构交互,其交互模式不同于一般队列(和 Azure 事件中心的情况一样)。It can also be used for interaction with messaging infrastructure where the interaction patterns are different from regular queues, as is the case with Azure Event Hubs. 当事件发送到事件中心时,事件中心的作用如同队列,但从中读取事件时,其作用更类似于串行存储服务;它有点类似于磁带机。An Event Hub acts like a queue when events are sent to it, but acts more like a serial storage service when events are read from it; it somewhat resembles a tape drive. 客户端选择可用数据流的偏移,并获取从该偏移位置转到最新可用的所有事件。The client picks an offset into the available data stream and is then served all events from that offset to the latest available.

AMQP 1.0 协议被设计为可扩展,允许进一步规范以增强其功能。The AMQP 1.0 protocol is designed to be extensible, enabling further specifications to enhance its capabilities. 本文档中介绍的三个扩展规范会说明这点。The three extension specifications discussed in this document illustrate this. 在对于通过现有 HTTPS/WebSockets 基础结构进行的通信,可能难以配置本机 AMQP TCP 端口。For communication over existing HTTPS/WebSockets infrastructure, configuring the native AMQP TCP ports may be difficult. 绑定规范定义如何通过 WebSockets 将 AMQP 分层。A binding specification defines how to layer AMQP over WebSockets. 以请求/响应方式与消息传送基础结构交互,以便进行管理或提供高级功能时,AMQP 管理规范定义所需的基本交互基元。For interacting with the messaging infrastructure in a request/response fashion for management purposes or to provide advanced functionality, the AMQP management specification defines the required basic interaction primitives. 在联合授权模型集成中,AMQP 基于声明的安全规范定义如何关联和续订与链接关联的授权令牌。For federated authorization model integration, the AMQP claims-based-security specification defines how to associate and renew authorization tokens associated with links.

基本 AMQP 方案Basic AMQP scenarios

本部分说明 AMQP 1.0 与 Azure 服务总线的基本使用方式,其中包括创建连接、会话和链接,以及与服务总线实体(例如队列、主题和订阅)相互传输消息。This section explains the basic usage of AMQP 1.0 with Azure Service Bus, which includes creating connections, sessions, and links, and transferring messages to and from Service Bus entities such as queues, topics, and subscriptions.

了解 AMQP 工作原理的最权威来源是 AMQP 1.0 规范,但此规范是为了精确引导实现而编写,而非用于传授协议知识。The most authoritative source to learn about how AMQP works is the AMQP 1.0 specification, but the specification was written to precisely guide implementation and not to teach the protocol. 本部分着重于尽可能介绍描述服务总线如何使用 AMQP 1.0 的术语。This section focuses on introducing as much terminology as needed for describing how Service Bus uses AMQP 1.0.

连接和会话Connections and sessions

AMQP 将通信程序称为容器 ;其中包含节点 ,即这些容器内的通信实体。AMQP calls the communicating programs containers; those contain nodes, which are the communicating entities inside of those containers. 队列可以是此类节点。A queue can be such a node. AMQP 允许多路复用,因此单个连接可用于节点之间的许多通信路径;例如,应用程序客户端可以同时从一个队列接收,并通过相同的网络连接发送到另一个队列。AMQP allows for multiplexing, so a single connection can be used for many communication paths between nodes; for example, an application client can concurrently receive from one queue and send to another queue over the same network connection.

示意图,显示在不同容器之间进行的会话和连接。

网络连接因此固定在容器上。The network connection is thus anchored on the container. 它由采用客户端角色的容器启动,对采用接收者角色的容器进行出站 TCP 套接字连接,以侦听和接受入站 TCP 连接。It is initiated by the container in the client role making an outbound TCP socket connection to a container in the receiver role, which listens for and accepts inbound TCP connections. 连接握手包括协商协议版本,声明或协商传输级别安全性 (TLS/SSL) 的使用,以及基于 SASL 的连接范围的身份验证/授权握手。The connection handshake includes negotiating the protocol version, declaring or negotiating the use of Transport Level Security (TLS/SSL), and an authentication/authorization handshake at the connection scope that is based on SASL.

Azure 服务总线随时都需要使用 TLS。Azure Service Bus requires the use of TLS at all times. 它支持通过 TCP 端口 5671 的连接,因此 TCP 连接在进入 AMQP 协议握手前先与 TLS 重叠;它还支持通过 TCP 端口 5672 的连接,因此服务器使用 AMQP 规定的模型,立即提供强制的 TLS 连接升级。It supports connections over TCP port 5671, whereby the TCP connection is first overlaid with TLS before entering the AMQP protocol handshake, and also supports connections over TCP port 5672 whereby the server immediately offers a mandatory upgrade of connection to TLS using the AMQP-prescribed model. AMQP WebSockets 绑定创建基于 TCP 端口 443 的隧道,其相当于 AMQP 5671 连接。The AMQP WebSockets binding creates a tunnel over TCP port 443 that is then equivalent to AMQP 5671 connections.

在设置连接和 TLS 之后,服务总线提供两个 SASL 机制选项:After setting up the connection and TLS, Service Bus offers two SASL mechanism options:

  • SASL PLAIN 常用于将用户名和密码凭据发送到服务器。SASL PLAIN is commonly used for passing username and password credentials to a server. 服务总线没有帐户,但是有命名的共享访问安全规则,这些规则可授予权限并与某个密钥关联。Service Bus does not have accounts, but named Shared Access Security rules, which confer rights and are associated with a key. 规则名称可作为用户名,而密钥(如 base64 编码文本)可作为密码。The name of a rule is used as the user name and the key (as base64 encoded text) is used as the password. 与所选规则关联的权限控管允许在连接上进行的操作。The rights associated with the chosen rule govern the operations allowed on the connection.
  • 当客户端想要使用稍后所述的基于声明的安全 (CBS) 模型时,SASL ANONYMOUS 可用于绕过 SASL 授权。SASL ANONYMOUS is used for bypassing SASL authorization when the client wants to use the claims-based-security (CBS) model that is described later. 使用此选项,即可以匿名方式创建短期的客户端连接,在此连接期间,客户端只能与 CBS 终结点交互,并且 CBS 握手必须完成。With this option, a client connection can be established anonymously for a short time during which the client can only interact with the CBS endpoint and the CBS handshake must complete.

创建传输连接之后,容器各自声明它们愿意处理的帧大小上限;在空闲超时后,如果连接上没有任何活动,它们会单方面断开连接。After the transport connection is established, the containers each declare the maximum frame size they are willing to handle, and after an idle timeout they'll unilaterally disconnect if there is no activity on the connection.

它们也声明支持的并发通道数量。They also declare how many concurrent channels are supported. 通道是基于连接的单向出站虚拟传输路径。A channel is a unidirectional, outbound, virtual transfer path on top of the connection. 会话从每个互连的容器获取通道,以形成双向通信路径。A session takes a channel from each of the interconnected containers to form a bi-directional communication path.

会话具有基于时段的流量控制模型;创建会话时,每一方声明它愿意在接收时段内接受的帧数。Sessions have a window-based flow control model; when a session is created, each party declares how many frames it is willing to accept into its receive window. 当各方交换帧时,已传输的帧将填满该时段,传输在时段已满时停止,直到该时段使用流程行为原语进行重置或扩展为止(行为原语是 AMQP 术语,表示在双方之间交换的协议级别手势)。As the parties exchange frames, transferred frames fill that window and transfers stop when the window is full and until the window gets reset or expanded using the flow performative (performative is the AMQP term for protocol-level gestures exchanged between the two parties).

这种基于时段的模型大致类似于 TCP 基于时段的流量控制概念,但属于套接字内的会话级别。This window-based model is roughly analogous to the TCP concept of window-based flow control, but at the session level inside the socket. 协议提出了一个概念:允许多个并发会话,因此高优先级流量的速度可能快于受限制的正常流量,就像高速公路上有一个快速车道一样。The protocol's concept of allowing for multiple concurrent sessions exists so that high priority traffic could be rushed past throttled normal traffic, like on a highway express lane.

Azure 服务总线目前只对每个连接使用一个会话。Azure Service Bus currently uses exactly one session for each connection. 服务总线标准版和事件中心的服务总线帧大小上限为 262,144 字节 (256 KB)。The Service Bus maximum frame-size is 262,144 bytes (256-K bytes) for Service Bus Standard and Event Hubs. 服务总线高级版则为 1,048,576 (1 MB)。It is 1,048,576 (1 MB) for Service Bus Premium. 服务总线不强加任何特定会话级别限制时段,但是在链接级别流量控制中定期重置时段(请参阅下一部分)。Service Bus does not impose any particular session-level throttling windows, but resets the window regularly as part of link-level flow control (see the next section).

连接、通道和会话是暂时性的。Connections, channels, and sessions are ephemeral. 如果基础连接失效,则必须重新创建连接、TLS 隧道、SASL 授权上下文和会话。If the underlying connection collapses, connections, TLS tunnel, SASL authorization context, and sessions must be reestablished.

AMQP 出站端口要求AMQP outbound port requirements

通过 TCP 使用 AMQP 连接的客户端需要在本地防火墙中打开端口 5671 和 5672。Clients that use AMQP connections over TCP require ports 5671 and 5672 to be opened in the local firewall. 如果启用了 EnableLinkRedirect 功能,除了这些端口,可能还需要打开其他端口。Along with these ports, it might be necessary to open additional ports if the EnableLinkRedirect feature is enabled. EnableLinkRedirect 是一项新的消息传递功能,它可以在接收消息时跳过一个跃点,从而有助于提高吞吐量。EnableLinkRedirect is a new messaging feature that helps skip one-hop while receiving messages, thus helping to boost throughput. 客户端将开始通过端口范围 104XX 直接与后端服务通信,如下图所示。The client would start communicating directly with the back-end service over port range 104XX as shown in the following image.

目标端口列表

如果防火墙阻止了这些端口,则 .NET 客户端将失败,并返回 SocketException(“试图以访问权限所禁止的方式访问套接字”)。A .NET client would fail with a SocketException ("An attempt was made to access a socket in a way forbidden by its access permissions") if these ports are blocked by the firewall. 可以通过在连接字符串中设置 EnableAmqpLinkRedirect=false 来禁用该功能,这将强制客户端通过端口 5671 与远程服务进行通信。The feature can be disabled by setting EnableAmqpLinkRedirect=false in the connectiong string, which forces the clients to communicate with the remote service over port 5671.

AMQP 通过链接传输消息。AMQP transfers messages over links. 链接是在能以单个方向传输消息的会话中创建的通信路径;传输状态协商通过链接在已连接方之间双向进行。A link is a communication path created over a session that enables transferring messages in one direction; the transfer status negotiation is over the link and bi-directional between the connected parties.

屏幕截图,显示在两个容器之间进行链接连接的会话。

任一容器可以在现有的会话中随时创建链接,这使 AMQP 不同于其他许多协议(包括 HTTP 和 MQTT),其中启动传输和传输路径是创建套接字连接之一方的独占权限。Links can be created by either container at any time and over an existing session, which makes AMQP different from many other protocols, including HTTP and MQTT, where the initiation of transfers and transfer path is an exclusive privilege of the party creating the socket connection.

启动链接的容器请求相对的容器接受链接,并且选择发送者或接收者的角色。The link-initiating container asks the opposite container to accept a link and it chooses a role of either sender or receiver. 因此,任一容器均可启动创建单向或双向通信路径,而后者建模为成对链接。Therefore, either container can initiate creating unidirectional or bi-directional communication paths, with the latter modeled as pairs of links.

链接具有名称并与节点关联。Links are named and associated with nodes. 如一开始所述,节点是容器内的通信实体。As stated in the beginning, nodes are the communicating entities inside a container.

在服务总线中,节点直接等同于队列、主题、订阅,或队列或订阅的死信子队列。In Service Bus, a node is directly equivalent to a queue, a topic, a subscription, or a deadletter subqueue of a queue or subscription. AMQP 中使用的节点名称因此是服务总线命名空间内实体的相对名称。The node name used in AMQP is therefore the relative name of the entity inside of the Service Bus namespace. 如果队列名为 myqueue,则该名称也是它的 AMQP 节点名称。If a queue is named myqueue, that's also its AMQP node name. 主题订阅遵循 HTTP API 约定归类为“订阅”资源集合,因此主题 mytopic 上的订阅 sub 具有 AMQP 节点名称 mytopic/subscriptions/subA topic subscription follows the HTTP API convention by being sorted into a "subscriptions" resource collection and thus, a subscription sub on a topic mytopic has the AMQP node name mytopic/subscriptions/sub.

正在连接的客户端也必须使用本地节点名称来创建链接;服务总线不规范这些节点名称,并且不进行解释。The connecting client is also required to use a local node name for creating links; Service Bus is not prescriptive about those node names and does not interpret them. AMQP 1.0 客户端堆栈通常使用方案,以确保这些暂时节点名称是客户端范围内的唯一名称。AMQP 1.0 client stacks generally use a scheme to assure that these ephemeral node names are unique in the scope of the client.

传输Transfers

建立链接后,即可通过该链接传输消息。Once a link has been established, messages can be transferred over that link. 在 AMQP 中,使用明确的协议手势运行传输(传输行为原语),以通过链接将消息从发送者转到接收者。In AMQP, a transfer is executed with an explicit protocol gesture (the transfer performative) that moves a message from sender to receiver over a link. 传输在“安置好”时完成,这意味着双方已建立该传输结果的共识。A transfer is complete when it is "settled", meaning that both parties have established a shared understanding of the outcome of that transfer.

示意图,显示在发送方与接收方之间进行的消息传输以及由此进行的处理。

在最简单的情况下,发送者可以选择发送“预先安置”的消息,这意味着客户端对结果不感兴趣,并且接收者不提供任何有关操作结果的反馈。In the simplest case, the sender can choose to send messages "pre-settled," meaning that the client isn't interested in the outcome and the receiver does not provide any feedback about the outcome of the operation. 此模式由服务总线在 AMQP 协议级别支持,但不显示在任何客户端 API 中。This mode is supported by Service Bus at the AMQP protocol level, but not exposed in any of the client APIs.

一般情况是发送未安置好的消息,并且接收者使用处置行为原语表示接受或拒绝。The regular case is that messages are being sent unsettled, and the receiver then indicates acceptance or rejection using the disposition performative. 拒绝发生于接收者因为任何原因而无法接受消息时,而拒绝消息包含原因相关信息,这是 AMQP 所定义的错误结构。Rejection occurs when the receiver cannot accept the message for any reason, and the rejection message contains information about the reason, which is an error structure defined by AMQP. 如果消息因为服务总线内部的错误而被拒绝,则服务返回该结构内的额外信息,而如果发出支持请求,该信息即可用于提供诊断提示给支持人员。If messages are rejected due to internal errors inside of Service Bus, the service returns extra information inside that structure that can be used for providing diagnostics hints to support personnel if you are filing support requests. 稍后介绍有关错误的更多详细信息。You learn more details about errors later.

“已解除”状态是一种特殊形式的拒绝,表示接收者对传输没有任何技术异议,但对于安置传输也不感兴趣。A special form of rejection is the released state, which indicates that the receiver has no technical objection to the transfer, but also no interest in settling the transfer. 该情况的确存在,例如,当消息传递到服务总线客户端,而客户端因为无法运行处理消息所生成的任务(尽管消息传递本身并未出错)而选择“放弃”消息时。That case exists, for example, when a message is delivered to a Service Bus client, and the client chooses to "abandon" the message because it cannot perform the work resulting from processing the message; the message delivery itself is not at fault. 该状态的一个变体是“已修改”状态(这种状态允许在消息解除后进行更改)。A variation of that state is the modified state, which allows changes to the message as it is released. 服务总线目前不使用该状态。That state is not used by Service Bus at present.

AMQP 1.0 规范定义进一步的处置状态(称为“已接收”),其对于处理链接恢复特别有用。The AMQP 1.0 specification defines a further disposition state called received, that specifically helps to handle link recovery. 当以前的连接和会话丢失时,链接恢复允许重组链接的状态,以及新连接和会话的任何搁置传递。Link recovery allows reconstituting the state of a link and any pending deliveries on top of a new connection and session, when the prior connection and session were lost.

服务总线不支持链接恢复;如果客户端失去对服务总线的连接,并且未安置的消息传输已搁置,该消息传输便丢失,而客户端必须重新连接、重建链接,以及重试传输。Service Bus does not support link recovery; if the client loses the connection to Service Bus with an unsettled message transfer pending, that message transfer is lost, and the client must reconnect, reestablish the link, and retry the transfer.

同样地,服务总线和事件中心都支持“至少一次”传输,其中的发送者可确保消息已存储并接受,但是不支持在 AMQP 级别的“刚好一次”传输,其中的系统尝试恢复链接并继续协商传递状态,以避免重复传输消息。As such, Service Bus and Event Hubs support "at least once" transfer where the sender can be assured for the message having been stored and accepted, but do not support "exactly once" transfers at the AMQP level, where the system would attempt to recover the link and continue to negotiate the delivery state to avoid duplication of the message transfer.

为了弥补可能的重复发送,服务总线支持重复检测队列和主题(可选功能)。To compensate for possible duplicate sends, Service Bus supports duplicate detection as an optional feature on queues and topics. 重复检测在用户定义的时段内记录所有传入消息的消息 ID,并以无消息方式放弃在该相同时段内使用相同消息 ID 发送的所有消息。Duplicate detection records the message IDs of all incoming messages during a user-defined time window, then silently drops all messages sent with the same message-IDs during that same window.

流量控制Flow control

除了以前介绍过的会话级别流量控制模型以外,每个链接都有自己的流量控制模型。In addition to the session-level flow control model that previously discussed, each link has its own flow control model. 会话级别流量控制可防止容器必须一次处理太多帧,链接级别流量控制让应用程序负责控制它想要从链接处理的消息数目以及时机。Session-level flow control protects the container from having to handle too many frames at once, link-level flow control puts the application in charge of how many messages it wants to handle from a link and when.

日志的屏幕截图,显示源、目标、源端口、目标端口和协议名称。

在链接上,传输只发生于发送者有足够的“链接信用额度”时。On a link, transfers can only happen when the sender has enough link credit. 链接信用额度是接收者使用 流程 行为原语所设置的计数器,其范围是链接。Link credit is a counter set by the receiver using the flow performative, which is scoped to a link. 将链接信用额度分配给发送者时,将通过传递消息来尝试用完该信用额度。When the sender is assigned link credit, it attempts to use up that credit by delivering messages. 每个消息传递使剩余的链接信用额度减 1。Each message delivery decrements the remaining link credit by 1. 当链接信用额度用完时,便会停止传递。When the link credit is used up, deliveries stop.

当服务总线采用接收者角色时,则立即提供给发送者充足的链接信用额度,以便立即发送消息。When Service Bus is in the receiver role, it instantly provides the sender with ample link credit, so that messages can be sent immediately. 使用链接信用额度时,服务总线偶尔发送“流程”行为原语给发送者,以更新链接信用额度余额。As link credit is used, Service Bus occasionally sends a flow performative to the sender to update the link credit balance.

采用发送者角色时,服务总线发送消息,用完任何未偿付的链接信用额度。In the sender role, Service Bus sends messages to use up any outstanding link credit.

在 API 级别的“接收”调用转译成由客户端发送到服务总线的“流”行为原语,而服务总线获取队列中第一个可用的未锁定消息,进行锁定并传输,以使用该信用额度。A "receive" call at the API level translates into a flow performative being sent to Service Bus by the client, and Service Bus consumes that credit by taking the first available, unlocked message from the queue, locking it, and transferring it. 如果没有可立即传递的消息,由任何链接使用该特定实体创建的任何未偿付信用额度仍以抵达顺序记录,而消息遭到锁定并且在能够使用任何未偿付信用额度时传输。If there is no message readily available for delivery, any outstanding credit by any link established with that particular entity remains recorded in order of arrival, and messages are locked and transferred as they become available, to use any outstanding credit.

当传输进入“已接受”、“已拒绝”或“已解除”终端状态的其中一种时,消息锁定就会解除。The lock on a message is released when the transfer is settled into one of the terminal states accepted, rejected, or released. 终端的状态为“已接受”时,将从服务总线中删除消息。The message is removed from Service Bus when the terminal state is accepted. 它保留在服务总线中,并会在传输达到任何其他状态时传递给下一个接收者。It remains in Service Bus and is delivered to the next receiver when the transfer reaches any of the other states. 服务总线在因为重复拒绝或解除而达到实体所允许的最大传递计数时,自动将消息转到实体死信队列中。Service Bus automatically moves the message into the entity's deadletter queue when it reaches the maximum delivery count allowed for the entity due to repeated rejections or releases.

即使是服务总线 API 现今也不直接公开这种选项,较低级别的 AMQP 协议客户端可以使用链接信用额度模型,通过核发大量的链接信用额度,将针对每个接收请求核发一单位信用额度的“提取式”模型变成“推送式”模型,并接收可用的消息,而不需要任何进一步的交互。Even though the Service Bus APIs do not directly expose such an option today, a lower-level AMQP protocol client can use the link-credit model to turn the "pull-style" interaction of issuing one unit of credit for each receive request into a "push-style" model by issuing a large number of link credits and then receive messages as they become available without any further interaction. 通过 MessagingFactory.PrefetchCountMessageReceiver.PrefetchCount 属性设置来支持推送。Push is supported through the MessagingFactory.PrefetchCount or MessageReceiver.PrefetchCount property settings. 如果两者均不为零,则 AMQP 客户端使用它作为链接信用额度。When they are non-zero, the AMQP client uses it as the link credit.

在此内容中,务必了解实体内消息锁定的过期时钟在从实体获取消息时启动,而不是在消息放在网络上时启动。In this context, it's important to understand that the clock for the expiration of the lock on the message inside the entity starts when the message is taken from the entity, not when the message is put on the wire. 每当客户端通过颁发链接信用额度来表示接收消息的整备性,因此预期主动提取网络上的消息并准备好处理它们。Whenever the client indicates readiness to receive messages by issuing link credit, it is therefore expected to be actively pulling messages across the network and be ready to handle them. 否则消息锁定可能在消息传递之前过期。Otherwise the message lock may have expired before the message is even delivered. 使用链接信用流量控制应直接反映出可立即准备处理分派给接收者的可用消息。The use of link-credit flow control should directly reflect the immediate readiness to deal with available messages dispatched to the receiver.

以下部分汇总了在不同 API 交互期间行为原语流程的图解概述。In summary, the following sections provide a schematic overview of the performative flow during different API interactions. 每部分说明不同的逻辑操作。Each section describes a different logical operation. 其中一些交互可能很“缓慢”,这意味着它们可能只在有需要时运行。Some of those interactions may be "lazy," meaning they may only be performed when required. 直到发送或请求第一个消息,创建消息发送者才造成网络交互。Creating a message sender may not cause a network interaction until the first message is sent or requested.

下表中的箭头显示行为原语流程方向。The arrows in the following table show the performative flow direction.

创建消息接收者Create message receiver

客户端Client 服务总线Service Bus
--> attach(--> attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=receiver,role=receiver,
source={entity name},source={entity name},
target={client link ID}target={client link ID}
))
客户端作为接收者附加到实体Client attaches to entity as receiver
附加到链接末尾的服务总线回复Service Bus replies attaching its end of the link <-- attach(<-- attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=sender,role=sender,
source={entity name},source={entity name},
target={client link ID}target={client link ID}
))

创建消息发送者Create message sender

客户端Client 服务总线Service Bus
--> attach(--> attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=sender,role=sender,
source={client link ID},source={client link ID},
target={entity name}target={entity name}
))
无操作No action
无操作No action <-- attach(<-- attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=receiver,role=receiver,
source={client link ID},source={client link ID},
target={entity name}target={entity name}
))

创建消息发送者(错误)Create message sender (error)

客户端Client 服务总线Service Bus
--> attach(--> attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=sender,role=sender,
source={client link ID},source={client link ID},
target={entity name}target={entity name}
))
无操作No action
无操作No action <-- attach(<-- attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=receiver,role=receiver,
source=null,source=null,
target=nulltarget=null
))

<-- detach(<-- detach(
handle={numeric handle},handle={numeric handle},
closed=true,closed=true,
error={error info}error={error info}
))

关闭消息接收者/发送者Close message receiver/sender

客户端Client 服务总线Service Bus
--> detach(--> detach(
handle={numeric handle},handle={numeric handle},
closed=trueclosed=true
))
无操作No action
无操作No action <-- detach(<-- detach(
handle={numeric handle},handle={numeric handle},
closed=trueclosed=true
))

发送(成功)Send (success)

客户端Client 服务总线Service Bus
--> transfer(--> transfer(
delivery-id={numeric handle},delivery-id={numeric handle},
delivery-tag={binary handle},delivery-tag={binary handle},
settled=false,,more=false,settled=false,,more=false,
state=null,state=null,
resume=falseresume=false
))
无操作No action
无操作No action <-- disposition(<-- disposition(
role=receiver,role=receiver,
first={delivery ID},first={delivery ID},
last={delivery ID},last={delivery ID},
settled=true,settled=true,
state=acceptedstate=accepted
))

发送(错误)Send (error)

客户端Client 服务总线Service Bus
--> transfer(--> transfer(
delivery-id={numeric handle},delivery-id={numeric handle},
delivery-tag={binary handle},delivery-tag={binary handle},
settled=false,,more=false,settled=false,,more=false,
state=null,state=null,
resume=falseresume=false
))
无操作No action
无操作No action <-- disposition(<-- disposition(
role=receiver,role=receiver,
first={delivery ID},first={delivery ID},
last={delivery ID},last={delivery ID},
settled=true,settled=true,
state=rejected(state=rejected(
error={error info}error={error info}
))
))

接收Receive

客户端Client 服务总线Service Bus
--> flow(--> flow(
link-credit=1link-credit=1
))
无操作No action
无操作No action < transfer(< transfer(
delivery-id={numeric handle},delivery-id={numeric handle},
delivery-tag={binary handle},delivery-tag={binary handle},
settled=false,settled=false,
more=false,more=false,
state=null,state=null,
resume=falseresume=false
))
--> disposition(--> disposition(
role=receiver,role=receiver,
first={delivery ID},first={delivery ID},
last={delivery ID},last={delivery ID},
settled=true,settled=true,
state=acceptedstate=accepted
))
无操作No action

多消息接收Multi-message receive

客户端Client 服务总线Service Bus
--> flow(--> flow(
link-credit=3link-credit=3
))
无操作No action
无操作No action < transfer(< transfer(
delivery-id={numeric handle},delivery-id={numeric handle},
delivery-tag={binary handle},delivery-tag={binary handle},
settled=false,settled=false,
more=false,more=false,
state=null,state=null,
resume=falseresume=false
))
无操作No action < transfer(< transfer(
delivery-id={numeric handle+1},delivery-id={numeric handle+1},
delivery-tag={binary handle},delivery-tag={binary handle},
settled=false,settled=false,
more=false,more=false,
state=null,state=null,
resume=falseresume=false
))
无操作No action < transfer(< transfer(
delivery-id={numeric handle+2},delivery-id={numeric handle+2},
delivery-tag={binary handle},delivery-tag={binary handle},
settled=false,settled=false,
more=false,more=false,
state=null,state=null,
resume=falseresume=false
))
--> disposition(--> disposition(
role=receiver,role=receiver,
first={delivery ID},first={delivery ID},
last={delivery ID+2},last={delivery ID+2},
settled=true,settled=true,
state=acceptedstate=accepted
))
无操作No action

消息Messages

以下部分说明服务总线使用标准 AMQP 消息部分中的哪些属性,以及它们如何映射到服务总线 API 集。The following sections explain which properties from the standard AMQP message sections are used by Service Bus and how they map to the Service Bus API set.

任何应用程序需要定义的属性都应映射至 AMQP 的 application-properties 映射。Any property that application needs to defines should be mapped to AMQP's application-properties map.

字段名称Field Name 使用情况Usage API 名称API name
durabledurable - -
prioritypriority - -
ttlttl 消息生存时间Time to live for this message TimeToLiveTimeToLive
first-acquirerfirst-acquirer - -
delivery-countdelivery-count - DeliveryCountDeliveryCount

propertiesproperties

字段名称Field Name 使用情况Usage API 名称API name
message-idmessage-id 应用程序为此消息定义的自由格式标识符。Application-defined, free-form identifier for this message. 用于重复检测。Used for duplicate detection. MessageIdMessageId
user-iduser-id 应用程序定义的用户标识符,服务总线无法进行解释。Application-defined user identifier, not interpreted by Service Bus. 无法通过服务总线 API 访问。Not accessible through the Service Bus API.
toto 应用程序定义的目标标识符,服务总线无法进行解释。Application-defined destination identifier, not interpreted by Service Bus. ToTo
subjectsubject 应用程序定义的消息用途标识符,服务总线无法进行解释。Application-defined message purpose identifier, not interpreted by Service Bus. LabelLabel
reply-toreply-to 应用程序定义的回复路径指示符,服务总线无法进行解释。Application-defined reply-path indicator, not interpreted by Service Bus. ReplyToReplyTo
correlation-idcorrelation-id 应用程序定义的相关性标识符,服务总线无法进行解释。Application-defined correlation identifier, not interpreted by Service Bus. CorrelationIdCorrelationId
content-typecontent-type 应用程序定义的内容类型指示符,服务总线无法进行解释。Application-defined content-type indicator for the body, not interpreted by Service Bus. ContentTypeContentType
content-encodingcontent-encoding 应用程序定义的内容编码指示符,服务总线无法进行解释。Application-defined content-encoding indicator for the body, not interpreted by Service Bus. 无法通过服务总线 API 访问。Not accessible through the Service Bus API.
absolute-expiry-timeabsolute-expiry-time 声明消息过期的绝对时刻。Declares at which absolute instant the message expires. 在输入时忽略(观察到标头 TTL),在输出时授权具权威性。Ignored on input (header TTL is observed), authoritative on output. ExpiresAtUtcExpiresAtUtc
creation-timecreation-time 声明消息的创建时间。Declares at which time the message was created. 不由服务总线使用Not used by Service Bus 无法通过服务总线 API 访问。Not accessible through the Service Bus API.
group-idgroup-id 应用程序为相关的消息集定义的标识符。Application-defined identifier for a related set of messages. 用于服务总线会话。Used for Service Bus sessions. SessionIdSessionId
group-sequencegroup-sequence 用于标识消息在会话内的相对序列号的计数器。Counter identifying the relative sequence number of the message inside a session. 服务总线会将其忽略。Ignored by Service Bus. 无法通过服务总线 API 访问。Not accessible through the Service Bus API.
reply-to-group-idreply-to-group-id - ReplyToSessionIdReplyToSessionId

消息注释Message annotations

存在几个不属于 AMQP 消息属性的其他服务总线消息属性,且它们在消息上作为 MessageAnnotations 传递。There are few other service bus message properties, which are not part of AMQP message properties, and are passed along as MessageAnnotations on the message.

注释映射键Annotation Map Key 使用情况Usage API 名称API name
x-opt-scheduled-enqueue-timex-opt-scheduled-enqueue-time 声明消息应于何时出现在实体上Declares at which time the message should appear on the entity ScheduledEnqueueTimeScheduledEnqueueTime
x-opt-partition-keyx-opt-partition-key 应用程序定义的键,指示消息应进入哪个分区。Application-defined key that dictates which partition the message should land in. PartitionKeyPartitionKey
x-opt-via-partition-keyx-opt-via-partition-key 应用程序定义的分区键值,指示某个事务在何时用于通过传输队列发送消息。Application-defined partition-key value when a transaction is to be used to send messages via a transfer queue. ViaPartitionKeyViaPartitionKey
x-opt-enqueued-timex-opt-enqueued-time 服务定义的 UTC 时间,代表将消息加入队列的实际时间。Service-defined UTC time representing the actual time of enqueuing the message. 输入时忽略。Ignored on input. EnqueuedTimeUtcEnqueuedTimeUtc
x-opt-sequence-numberx-opt-sequence-number 服务定义的唯一编号,用于分配给消息。Service-defined unique number assigned to a message. SequenceNumberSequenceNumber
x-opt-offsetx-opt-offset 服务定义的消息的排队序列号。Service-defined enqueued sequence number of the message. EnqueuedSequenceNumberEnqueuedSequenceNumber
x-opt-locked-untilx-opt-locked-until 服务定义。Service-defined. 日期和时间,在此之前消息将在队列/订阅中被锁定。The date and time until which the message will be locked in the queue/subscription. LockedUntilUtcLockedUntilUtc
x-opt-deadletter-sourcex-opt-deadletter-source 服务定义。Service-Defined. 原始消息的来源,前提是从死信队列中接收消息。If the message is received from dead letter queue, the source of the original message. DeadLetterSourceDeadLetterSource

事务功能Transaction capability

一个事务将两个或更多操作组合成执行作用域。A transaction groups two or more operations together into an execution scope. 就本质而言,此类事务必须确保所有操作属于给定的操作组,无论联合成功还是失败。By nature, such a transaction must ensure that all operations belonging to a given group of operations either succeed or fail jointly. 操作按标识符 txn-id 分组。The operations are grouped by an identifier txn-id.

对于事务性交互,客户端充当 transaction controller,控制着应该一起被分组的操作。For transactional interaction, the client acts as a transaction controller , which controls the operations that should be grouped together. 服务总线服务充当 transactional resource 并根据 transaction controller 的请求执行工作。Service Bus Service acts as a transactional resource and performs work as requested by the transaction controller.

客户端和服务通过该客户端建立的 control link 进行通信。The client and service communicate over a control link , which is established by the client. declaredischarge 消息由控制器通过控制链接发送,从而各自分配并完成事务(它们不代表事务性工作的划分)。The declare and discharge messages are sent by the controller over the control link to allocate and complete transactions respectively (they do not represent the demarcation of transactional work). 实际的发送/接收工作并不是在此链接上执行的。The actual send/receive is not performed on this link. 所请求的每个事务性操作通过所需 txn-id 显式标识,因此可能出现于连接上的任何链接。Each transactional operation requested is explicitly identified with the desired txn-id and therefore may occur on any link on the Connection. 如果关闭了控制链接,但是还存在它所创建的未释放事务,则所有此类事务都将立即回滚,且针对它们执行进一步的事务性工作的尝试都将导致失败。If the control link is closed while there exist non-discharged transactions it created, then all such transactions are immediately rolled back, and attempts to perform further transactional work on them will lead to failure. 不能预先确定控制链接上的消息。Messages on control link must not be pre settled.

每个连接都需要启动自己的控制链接,才能开始并结束事务。Every connection has to initiate its own control link to be able to start and end transactions. 该服务定义一个充当 coordinator 的特殊目标。The service defines a special target that functions as a coordinator. 客户端/控制器建立了指向该目标的控制链接。The client/controller establishes a control link to this target. 控制链接不受单个实体限制,即同样的控制链接可用于启动并释放多个实体的事务。Control link is outside the boundary of an entity, that is, same control link can be used to initiate and discharge transactions for multiple entities.

启动事务Starting a transaction

开始事务性工作。To begin transactional work. 控制器必须从协调器获取一个 txn-idthe controller must obtain a txn-id from the coordinator. 通过发送 declare 类型消息完成此操作。It does this by sending a declare type message. 如果声明成功,协调器会响应一个处置结果,其中包含分配的 txn-idIf the declaration is successful, the coordinator responds with a disposition outcome, which carries the assigned txn-id.

客户端(控制器)Client (Controller) 方向Direction 服务总线(协调器)Service Bus (Coordinator)
attach(attach(
name={link name},name={link name},
... ,... ,
role=sender,role=sender,
target=Coordinatortarget=Coordinator
))
------>
<------ attach(attach(
name={link name},name={link name},
... ,... ,
target=Coordinator()target=Coordinator()
))
transfer(transfer(
delivery-id=0, ...)delivery-id=0, ...)
{ AmqpValue (Declare() )}{ AmqpValue (Declare())}
------>
<------ disposition(disposition(
first=0, last=0,first=0, last=0,
state=Declared(state=Declared(
txn-id={transaction ID}txn-id={transaction ID}
))))

释放事务Discharging a transaction

控制器通过向协调器发送 discharge 消息来推断事务性工作。The controller concludes the transactional work by sending a discharge message to the coordinator. 控制器通过在释放正文上设置 fail 标志,指示它希望提交或回滚该事务性工作。The controller indicates that it wishes to commit or roll back the transactional work by setting the fail flag on the discharge body. 如果协调器无法完成释放操作,消息将被拒绝且结果中带有 transaction-errorIf the coordinator is unable to complete the discharge, the message is rejected with this outcome carrying the transaction-error.

请注意:fail=true 表示事务回滚,fail=false 表示提交。Note: fail=true refers to Rollback of a transaction, and fail=false refers to Commit.

客户端(控制器)Client (Controller) 方向Direction 服务总线(协调器)Service Bus (Coordinator)
transfer(transfer(
delivery-id=0, ...)delivery-id=0, ...)
{ AmqpValue (Declare())}{ AmqpValue (Declare())}
------>
<------ disposition(disposition(
first=0, last=0,first=0, last=0,
state=Declared(state=Declared(
txn-id={transaction ID}txn-id={transaction ID}
))))
.. .. ..
其他链接上的Transactional work
事务性工作on other links
.. .. ..
transfer(transfer(
delivery-id=57, ...)delivery-id=57, ...)
{ AmqpValue ({ AmqpValue (
Discharge(txn-id=0,
fail=false)
)}
Discharge(txn-id=0,
fail=false)
)}
------>
<------ disposition(disposition(
first=57, last=57,first=57, last=57,
state=Accepted() )state=Accepted())

在事务中发送消息Sending a message in a transaction

所有事务性工作都是通过包含 txn-id 的事务性传递状态 transactional-state 完成的。在发送消息时,transactional-state 位于消息的传输框架中。All transactional work is done with the transactional delivery state transactional-state that carries the txn-id. In the case of sending messages, the transactional-state is carried by the message's transfer frame.

客户端(控制器)Client (Controller) 方向Direction 服务总线(协调器)Service Bus (Coordinator)
transfer(transfer(
delivery-id=0, ...)delivery-id=0, ...)
{ AmqpValue (Declare())}{ AmqpValue (Declare())}
------>
<------ disposition(disposition(
first=0, last=0,first=0, last=0,
state=Declared(state=Declared(
txn-id={transaction ID}txn-id={transaction ID}
))))
transfer(transfer(
handle=1,handle=1,
delivery-id=1,delivery-id=1,
state=
TransactionalState(
txn-id=0)
)
state=
TransactionalState(
txn-id=0)
)

{ payload }{ payload }
------>
<------ disposition(disposition(
first=1, last=1,first=1, last=1,
state=TransactionalState(
txn-id=0,
outcome=Accepted()
))
state=TransactionalState(
txn-id=0,
outcome=Accepted()
))

在事务中处置消息Disposing a message in a transaction

消息处置包括类似 Complete / Abandon / DeadLetter / Defer 的操作。Message disposition includes operations like Complete / Abandon / DeadLetter / Defer. 若要在事务中执行这些操作,请通过 disposition 传递 transactional-stateTo perform these operations within a transaction, pass the transactional-state with the disposition.

客户端(控制器)Client (Controller) 方向Direction 服务总线(协调器)Service Bus (Coordinator)
transfer(transfer(
delivery-id=0, ...)delivery-id=0, ...)
{ AmqpValue (Declare())}{ AmqpValue (Declare())}
------>
<------ disposition(disposition(
first=0, last=0,first=0, last=0,
state=Declared(state=Declared(
txn-id={transaction ID}txn-id={transaction ID}
))))
<------ transfer(transfer(
handle=2,handle=2,
delivery-id=11,delivery-id=11,
state=null)state=null)
{ payload }{ payload }
disposition(disposition(
first=11, last=11,first=11, last=11,
state=TransactionalState(
txn-id=0,
outcome=Accepted()
))
state=TransactionalState(
txn-id=0,
outcome=Accepted()
))
------>

高级服务总线功能Advanced Service Bus capabilities

本部分介绍 Azure 服务总线的高级功能,这些功能基于 AMQP 的 OASIS 技术委员会目前正在开发的 AMQP 草稿扩展。This section covers advanced capabilities of Azure Service Bus that are based on draft extensions to AMQP, currently being developed in the OASIS Technical Committee for AMQP. 服务总线实现这些草稿的最新版本,并且采用这些草稿达到标准状态时所引进的更改。Service Bus implements the latest versions of these drafts and adopts changes introduced as those drafts reach standard status.

备注

服务总线消息高级操作通过请求/响应模式受到支持。Service Bus Messaging advanced operations are supported through a request/response pattern. 服务总线中的 AMQP 1.0:基于请求/响应的操作文章中详细介绍了这些操作。The details of these operations are described in the article AMQP 1.0 in Service Bus: request-response-based operations.

AMQP 管理AMQP management

AMQP 管理规范是本文中介绍的第一个草稿扩展。The AMQP management specification is the first of the draft extensions discussed in this article. 此规范定义一组基于 AMQP 协议的协议,以便通过 AMQP 进行消息基础结构的管理交互。This specification defines a set of protocols layered on top of the AMQP protocol that allow management interactions with the messaging infrastructure over AMQP. 此规范定义泛型操作(例如“创建”、“读取”、“更新”和“删除”),以便管理消息传送基础结构内的实体和一组查询操作。The specification defines generic operations such as create, read, update, and delete for managing entities inside a messaging infrastructure and a set of query operations.

上述所有手势都需要客户端与消息传送基础结构之间的请求/响应交互,因此此规范定义如何制作 AMQP 上交互模式的模型:客户端连接到消息传送基础结构、启动会话,并创建一组链接。All those gestures require a request/response interaction between the client and the messaging infrastructure, and therefore the specification defines how to model that interaction pattern on top of AMQP: the client connects to the messaging infrastructure, initiates a session, and then creates a pair of links. 在某一个链接上,客户端扮演发送者,而在其他链接上扮演接收者,因此创建一组可作为双向通道的链接。On one link, the client acts as sender and on the other it acts as receiver, thus creating a pair of links that can act as a bi-directional channel.

逻辑运算Logical Operation 客户端Client 服务总线Service Bus
创建请求响应路径Create Request Response Path --> attach(--> attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=sender,role=sender,
source=null,source=null,
target="myentity/$management"target="myentity/$management"
))
无操作No action
创建请求响应路径Create Request Response Path 无操作No action <-- attach(
name={link name},
<-- attach(
name={link name},

handle={numeric handle},handle={numeric handle},
role=receiver,role=receiver,
source=null,source=null,
target="myentity"target="myentity"
))
创建请求响应路径Create Request Response Path --> attach(--> attach(
name={link name},name={link name},
handle={numeric handle},handle={numeric handle},
role=receiver,role=receiver,
source="myentity/$management",source="myentity/$management",
target="myclient$id"target="myclient$id"
))
创建请求响应路径Create Request Response Path 无操作No action <-- attach(
name={link name},
<-- attach(
name={link name},

handle={numeric handle},handle={numeric handle},
role=sender,role=sender,
source="myentity",source="myentity",
target="myclient$id"target="myclient$id"
))

准备好该组链接,请求/响应实现就相当简单:请求是发送到消息传送基础结构内了解此模式之实体的消息。Having that pair of links in place, the request/response implementation is straightforward: a request is a message sent to an entity inside the messaging infrastructure that understands this pattern. 在该请求消息中,properties 部分中的 reply-to 字段设置为链接(此响应要传递到的链接)的 target 标识符。In that request-message, the reply-to field in the properties section is set to the target identifier for the link onto which to deliver the response. 处理实体会处理此请求,并通过“target”标识符匹配所示“reply-to”标识符的链接传递回复 。The handling entity processes the request, and then delivers the reply over the link whose target identifier matches the indicated reply-to identifier.

显然,该模式要求回复目标的客户端容器和客户端生成的标识符在所有客户端中是唯一的,并且出于安全原因,还要难以预测。The pattern obviously requires that the client container and the client-generated identifier for the reply destination are unique across all clients and, for security reasons, also difficult to predict.

用于管理协议和所有其他使用相同模式的协议的消息交换发生于应用程序级别;它们不定义新的 AMQP 协议级别手势。The message exchanges used for the management protocol and for all other protocols that use the same pattern happen at the application level; they do not define new AMQP protocol-level gestures. 这是刻意设计的,以便应用程序立即使用与 AMQP 1.0 堆栈兼容的这些扩展。That's intentional, so that applications can take immediate advantage of these extensions with compliant AMQP 1.0 stacks.

服务总线目前不实现管理规范的任何核心功能,但是对基于声明的安全性功能以及将在以下部分中介绍的几乎所有高级功能而言,管理规范所定义的请求/响应模式是重要的基础:Service Bus does not currently implement any of the core features of the management specification, but the request/response pattern defined by the management specification is foundational for the claims-based-security feature and for nearly all of the advanced capabilities discussed in the following sections:

基于声明的授权Claims-based authorization

AMQP 基于声明的授权 (CBS) 规范草案基于管理规范的请求/响应模式,主要说明如何配合使用联合安全令牌与 AMQP 的广义模型。The AMQP Claims-Based-Authorization (CBS) specification draft builds on the management specification request/response pattern, and describes a generalized model for how to use federated security tokens with AMQP.

简介中所述的 AMQP 默认安全模型基于 SASL,并与 AMQP 连接握手集成。The default security model of AMQP discussed in the introduction is based on SASL and integrates with the AMQP connection handshake. 使用 SASL 的好处是它提供已定义一组机制的可扩展模型,任何正式依赖 SASL 的协议均可受益。Using SASL has the advantage that it provides an extensible model for which a set of mechanisms have been defined from which any protocol that formally leans on SASL can benefit. 在这些机制之中,“PLAIN”用于传输用户名和密码,“EXTERNAL”用于绑定到 TLS 级别安全,“ANONYMOUS”用于表示缺少显式身份验证/授权,还有其他各种机制能够用于传递身份验证和/或授权凭据或令牌。Among those mechanisms are "PLAIN" for transfer of usernames and passwords, "EXTERNAL" to bind to TLS-level security, "ANONYMOUS" to express the absence of explicit authentication/authorization, and a broad variety of additional mechanisms that allow passing authentication and/or authorization credentials or tokens.

AMQP 的 SASL 集成有两个缺点:AMQP's SASL integration has two drawbacks:

  • 所有凭据与令牌的范围都只限于连接。All credentials and tokens are scoped to the connection. 消息传送基础结构可能需要根据每个实体提供不同的访问控制;例如,允许令牌的持有者发送到队列 A,而不是到队列 B。使用固定在连接上的授权上下文,就不可能使用单个连接并且对队列 A 和 B 使用不同的访问令牌。A messaging infrastructure may want to provide differentiated access control on a per-entity basis; for example, allowing the bearer of a token to send to queue A but not to queue B. With the authorization context anchored on the connection, it's not possible to use a single connection and yet use different access tokens for queue A and queue B.
  • 访问令牌的有效时间通常有限。Access tokens are typically only valid for a limited time. 此有效性要求用户定期重新获取令牌,而如果用户的访问权限已更改,令牌的颁发者便有机会拒绝颁发刷新令牌。This validity requires the user to periodically reacquire tokens and provides an opportunity to the token issuer to refuse issuing a fresh token if the user's access permissions have changed. AMQP 连接可能持续很长时间。AMQP connections may last for long periods of time. SASL 模型只提供一个机会在连接时设置令牌,这意味着消息传送基础结构必须在令牌过期时断开客户端的连接,或者必须接受允许与访问权限可能已在其间吊销的客户端持续通信的风险。The SASL model only provides a chance to set a token at connection time, which means that the messaging infrastructure either has to disconnect the client when the token expires or it needs to accept the risk of allowing continued communication with a client who's access rights may have been revoked in the interim.

服务总线实现的 AMQP CBS 规范可让这两个问题获得圆满的解决:它可让客户端创建访问令牌与每个节点的关联,以及在这些令牌过期前进行更新,而无需中断消息流。The AMQP CBS specification, implemented by Service Bus, enables an elegant workaround for both of those issues: It allows a client to associate access tokens with each node, and to update those tokens before they expire, without interrupting the message flow.

CBS 定义由消息传送基础结构所提供的虚拟管理节点(名为 $cbs)。CBS defines a virtual management node, named $cbs, to be provided by the messaging infrastructure. 管理节点可代表消息传送基础结构中的任何其他节点接受令牌。The management node accepts tokens on behalf of any other nodes in the messaging infrastructure.

协议手势是管理规范定义的请求/回复交换。The protocol gesture is a request/reply exchange as defined by the management specification. 这意味着客户端使用 $cbs 节点创建一组链接,在输出链接上传递请求,并在输入链接上等待响应。That means the client establishes a pair of links with the $cbs node and then passes a request on the outbound link, and then waits for the response on the inbound link.

请求消息具有以下应用程序属性:The request message has the following application properties:

Key 可选Optional 值类型Value Type 值内容Value Contents
operationoperation No stringstring put-tokenput-token
typetype No stringstring 正在放置的令牌类型。The type of the token being put.
namename No stringstring 令牌应用到的“受众”。The "audience" to which the token applies.
expirationexpiration Yes timestamptimestamp 令牌过期时间。The expiry time of the token.

name 属性标识应与此令牌关联的实体。The name property identifies the entity with which the token shall be associated. 在服务总线中,这是队列或主题/订阅的路径。In Service Bus it's the path to the queue, or topic/subscription. type 属性标识令牌类型:The type property identifies the token type:

令牌类型Token Type 令牌说明Token Description 正文类型Body Type 注释Notes
amqp:jwtamqp:jwt JSON Web 令牌 (JWT)JSON Web Token (JWT) AMQP 值(字符串)AMQP Value (string) 尚不可用。Not yet available.
amqp:swtamqp:swt 简单 Web 令牌 (SWT)Simple Web Token (SWT) AMQP 值(字符串)AMQP Value (string) 仅支持 AAD/ACS 颁发的 SWT 令牌Only supported for SWT tokens issued by AAD/ACS
servicebus.chinacloudapi.cn:sastokenservicebus.chinacloudapi.cn:sastoken 服务总线 SAS 令牌Service Bus SAS Token AMQP 值(字符串)AMQP Value (string) -

令牌赋予权限。Tokens confer rights. 服务总线识别三个基本权限:“发送”允许发送、“侦听”允许接收,“管理”允许操作实体。Service Bus knows about three fundamental rights: "Send" enables sending, "Listen" enables receiving, and "Manage" enables manipulating entities. AAD/ACS 颁发的 SWT 令牌明确将这些权限包含为声明。SWT tokens issued by AAD/ACS explicitly include those rights as claims. 服务总线 SAS 令牌引用在命名空间或实体上配置的规则,这些规则是使用权限配置的。Service Bus SAS tokens refer to rules configured on the namespace or entity, and those rules are configured with rights. 使用与该规则关联的密钥来签名令牌,以此方式让令牌表达各自的权限。Signing the token with the key associated with that rule thus makes the token express the respective rights. 与使用 put-token 的实体关联的令牌将允许已连接的客户端根据每个令牌权限来与实体交互。The token associated with an entity using put-token permits the connected client to interact with the entity per the token rights. 客户端承担 sender 角色的链接需要“发送”权限,而承担 receiver 角色的链接则需要“侦听”权限。A link where the client takes on the sender role requires the "Send" right; taking on the receiver role requires the "Listen" right.

回复消息具有以下 application-properties 值The reply message has the following application-properties values

Key 可选Optional 值类型Value Type 值内容Value Contents
status-codestatus-code No intint HTTP 响应代码 [RFC2616]HTTP response code [RFC2616].
status-descriptionstatus-description Yes stringstring 状态的说明。Description of the status.

客户端可以针对消息传送基础结构中的任何实体重复调用 put-tokenThe client can call put-token repeatedly and for any entity in the messaging infrastructure. 令牌的范围是当前客户端且定位点为当前连接,这意味着服务器在删除连接时会删除所有保留的令牌。The tokens are scoped to the current client and anchored on the current connection, meaning the server drops any retained tokens when the connection drops.

目前的服务总线实现只允许 CBS 配合SASL 方法“ANONYMOUS”。The current Service Bus implementation only allows CBS in conjunction with the SASL method "ANONYMOUS." 在 SASL 握手之前始终必须存在 SSL/TLS 连接。A SSL/TLS connection must always exist prior to the SASL handshake.

因此所选的 AMQP 1.0 客户端必须支持 ANONYMOUS 机制。The ANONYMOUS mechanism must therefore be supported by the chosen AMQP 1.0 client. 匿名访问表示发生初始连接握手(包括创建初始会话),而服务总线不知道谁正在创建此连接。Anonymous access means that the initial connection handshake, including creating of the initial session happens without Service Bus knowing who is creating the connection.

创建连接和会话后,将链接附加到 $cbs 节点和发送 put-token 请求是唯一允许的操作。Once the connection and session is established, attaching the links to the $cbs node and sending the put-token request are the only permitted operations. 必须在创建连接后的 20 秒内使用对某个实体节点的 put-token 请求成功创建有效的令牌,否则服务总线将单方面断开连接。A valid token must be set successfully using a put-token request for some entity node within 20 seconds after the connection has been established, otherwise the connection is unilaterally dropped by Service Bus.

客户端后续负责跟踪令牌过期时间。The client is subsequently responsible for keeping track of token expiration. 令牌过期时,服务总线立即删除相应实体连接上的所有链接。When a token expires, Service Bus promptly drops all links on the connection to the respective entity. 为防止出现问题,客户端随时可以通过具有相同 put-token 手势的虚拟 $cbs 管理节点,使用新的令牌来替换节点的令牌,且不干扰在不同链接上流动的有效负载流量。To prevent problem occurring, the client can replace the token for the node with a new one at any time through the virtual $cbs management node with the same put-token gesture, and without getting in the way of the payload traffic that flows on different links.

发送方式功能Send-via functionality

发送方式/传输发送者功能让服务总线能通过另一个实体将给定消息转发到目标实体。Send-via / Transfer sender is a functionality that lets service bus forward a given message to a destination entity through another entity. 此功能用于在单个事务中执行跨实体的操作。This feature is used to perform operations across entities in a single transaction.

借助此项功能,可以创建发送程序并建立指向 via-entity 的链接。With this functionality, you create a sender and establish the link to the via-entity. 在建立链接时,会传递其他信息以建立此链接上的消息/传输的正确目标。While establishing the link, additional information is passed to establish the true destination of the messages/transfers on this link. 附加成功后,此链接上发送的所有消息都会自动通过 via-entity 转发到 destination-entity 。Once the attach has been successful, all the messages sent on this link are automatically forwarded to the destination-entity through via-entity.

注意:在建立此链接前,via-entitydestination-entity 都需要通过身份验证。Note: Authentication has to be performed for both via-entity and destination-entity before establishing this link.

客户端Client 方向Direction 服务总线Service Bus
attach(attach(
name={link name},name={link name},
role=sender,role=sender,
source={client link ID},source={client link ID},
target= {via-entity} ,target={via-entity},
properties=map [(
com.microsoft:transfer-destination-address=
{destination-entity} )]
)
properties=map [(
com.microsoft:transfer-destination-address=
{destination-entity} )]
)
------>
<------ attach(attach(
name={link name},name={link name},
role=receiver,role=receiver,
source={client link ID},source={client link ID},
target={via-entity},target={via-entity},
properties=map [(properties=map [(
com.microsoft:transfer-destination-address=com.microsoft:transfer-destination-address=
{destination-entity} )] ){destination-entity} )] )

后续步骤Next steps

若要了解有关 AMQP 的详细信息,请访问以下链接:To learn more about AMQP, visit the following links: