消息会话Message sessions

使用 Azure 服务总线会话,可以连贯有序的方式处理一系列无限多的相关消息。Azure Service Bus sessions enable joint and ordered handling of unbounded sequences of related messages. 可以在“先进先出 (FIFO)”和“请求-响应”模式下使用会话。Sessions can be used in first in, first out (FIFO) and request-response patterns. 本文展示了如何在使用服务总线时使用会话来实现这些模式。This article shows how to use sessions to implement these patterns when using Service Bus.

备注

服务总线的基本层不支持会话。The basic tier of Service Bus doesn't support sessions. 标准层和高级层支持会话。The standard and premium tiers support sessions. 有关这些层之间的差异,请参阅服务总线定价For differences between these tiers, see Service Bus pricing.

先进先出 (FIFO) 模式First-in, first out (FIFO) pattern

若要在服务总线中实现 FIFO 保证,请使用会话。To realize a FIFO guarantee in Service Bus, use sessions. 服务总线没有规定消息之间的关系性质,也没有定义用于确定消息序列开始或结束位置的特定模型。Service Bus isn't prescriptive about the nature of the relationship between the messages, and also doesn't define a particular model for determining where a message sequence starts or ends.

任何发送程序都可以在将消息提交到主题或队列时创建会话,方法是将 SessionId 属性设置为会话专属的由应用程序定义的某标识符。Any sender can create a session when submitting messages into a topic or queue by setting the SessionId property to some application-defined identifier that is unique to the session. 在 AMQP 1.0 协议一级,此值映射到 group-id 属性。At the AMQP 1.0 protocol level, this value maps to the group-id property.

在会话感知队列或订阅中,如果有至少一个消息包含会话的 SessionId,会话就诞生了。On session-aware queues or subscriptions, sessions come into existence when there's at least one message with the session's SessionId. 一旦会话诞生,就没有规定会话何时过期或消失的已定义时间或 API。Once a session exists, there's no defined time or API for when the session expires or disappears. 理论上讲,服务总线认为,今天可以针对会话接收的消息,与一年时间内 SessionId 相同的下一个消息使用的会话是相同的。Theoretically, a message can be received for a session today, the next message in a year's time, and if the SessionId matches, the session is the same from the Service Bus perspective.

然而,通常情况下,应用程序都很清楚一组相关消息的开始和结束位置。Typically, however, an application has a clear notion of where a set of related messages starts and ends. 服务总线未设置任何具体规则。Service Bus doesn't set any specific rules.

例如,若要有意设置文件的传输序列,请将第一个、中间一个和最后一个消息的 Label 属性分别设置为 start、content 和 end。An example of how to delineate a sequence for transferring a file is to set the Label property for the first message to start, for intermediate messages to content, and for the last message to end. content 消息相对位置的计算方式为,当前消息的 SequenceNumber 与 start 消息的 SequenceNumber 的增量值。The relative position of the content messages can be computed as the current message SequenceNumber delta from the start message SequenceNumber.

借助服务总线中的会话功能,可以 C# 和 Java API 编写的 MessageSession 形式执行特定接收操作。The session feature in Service Bus enables a specific receive operation, in the form of MessageSession in the C# and Java APIs. 可以通过 Azure 资源管理器或在门户中设置标志,为队列或订阅设置 requiresSession 属性,从而启用此功能。You enable the feature by setting the requiresSession property on the queue or subscription via Azure Resource Manager, or by setting the flag in the portal. 若要尝试执行相关 API 操作,必须启用此功能。It's required before you attempt to use the related API operations.

在门户中,选中下图中展示的复选框设置标志:In the portal, set the flag with the following check box:

“创建队列”对话框的屏幕截图,其中的“启用会话”选项处于选中状态并用红色标出。

备注

在队列或订阅上启用会话后,客户端应用程序可能不再发送/接收常规消息。When Sessions are enabled on a queue or a subscription, the client applications can *no longer _ send/receive regular messages. 所有消息必须作为会话的一部分发送(通过设置会话 ID),并通过接收会话来接收。All messages must be sent as part of a session (by setting the session id) and received by receiving the session.

会话 API 存在于队列和订阅客户端上。The APIs for sessions exist on queue and subscription clients. 可以使用一个命令性模型来控制会话和消息的接收时间;还可以使用一个基于处理程序的模型(类似于 OnMessage)来简化接收循环的管理操作。There's an imperative model that controls when sessions and messages are received, and a handler-based model, similar to _OnMessage*, that hides the complexity of managing the receive loop.

会话功能Session features

会话支持对交错消息流进行并发解多路复用,同时保留和保证有序传递。Sessions provide concurrent de-multiplexing of interleaved message streams while preserving and guaranteeing ordered delivery.

示意图,显示会话功能如何保持按序送达。

MessageSession 接收程序是由接受会话的客户端创建。A MessageSession receiver is created by the client accepting a session. 客户端调用 C# 编写的 QueueClient.AcceptMessageSessionQueueClient.AcceptMessageSessionAsyncThe client calls QueueClient.AcceptMessageSession or QueueClient.AcceptMessageSessionAsync in C#. 在反应回调模型中,它会注册会话处理程序。In the reactive callback model, it registers a session handler.

MessageSession 对象被接受同时由客户端保留时,此客户端会对队列或订阅中的包含相应会话 SessionId 的所有消息,以及在会话保留期间仍在到达且包含相应 SessionId 的所有消息一直施加排他锁。When the MessageSession object is accepted and while it's held by a client, that client holds an exclusive lock on all messages with that session's SessionId that exist in the queue or subscription, and also on all messages with that SessionId that still arrive while the session is held.

调用 Close 或 CloseAsync 时,或当锁定期满导致应用程序无法执行关闭操作时,将会解除锁定 。The lock is released when Close or CloseAsync are called, or when the lock expires in cases in which the application is unable to do the close operation. 应将会话锁定视为对文件施加的排他锁。也就是说,应用程序应在不再需要它时和/或不需要再处理其他任何消息时关闭会话。The session lock should be treated like an exclusive lock on a file, meaning that the application should close the session as soon as it no longer needs it and/or doesn't expect any further messages.

当多个并发接收程序从队列中拉取消息时,属于特定会话的消息会被分派到当前让相应会话一直处于锁定状态的特定接收程序。When multiple concurrent receivers pull from the queue, the messages belonging to a particular session are dispatched to the specific receiver that currently holds the lock for that session. 通过此操作,位于一个队列或订阅中的交错消息流可以明确解多路复用到各个接收程序,这些接收程序也可以驻留在不同的客户端计算机上,因为锁定管理是在服务总线内的服务端执行。With that operation, an interleaved message stream in one queue or subscription is cleanly de-multiplexed to different receivers and those receivers can also live on different client machines, since the lock management happens service-side, inside Service Bus.

上图显示三个并发会话接收程序。The previous illustration shows three concurrent session receivers. 某个 SessionId = 4 的会话不具有活动的、所属的客户端,这意味着此特定会话不传递任何消息。One Session with SessionId = 4 has no active, owning client, which means that no messages are delivered from this specific session. 会话在很多方面都起着一个子队列的作用。A session acts in many ways like a sub queue.

会话接收程序保留的会话锁定是速览锁定安排模式使用的消息锁的保护伞。The session lock held by the session receiver is an umbrella for the message locks used by the peek-lock settlement mode. 只有一个接收程序可以在会话上使用锁。Only one receiver can have a lock on a session. 一个接收程序可能有许多正在传送中的消息,但会按顺序接收消息。A receiver may have many in-flight messages, but the messages will be received in order. 如果放弃消息,则会在下一次执行接收操作时再次处理相同的消息。Abandoning a message causes the same message to be served again with the next receive operation.

消息会话状态Message session state

在高度可缩放、高度可用的云系统中处理工作流时,与特定会话相关的工作流处理程序不仅必须能够从意外故障中恢复,还必须能够在不同的进程或计算机中从工作开始位置恢复部分完成的工作。When workflows are processed in high-scale, high-availability cloud systems, the workflow handler associated with a particular session must be able to recover from unexpected failures and can resume partially completed work on a different process or machine from where the work began.

借助会话状态工具,可以在中转站内为消息会话添加应用程序定义的注释。这样一来,当会话被新处理程序获取时,相对于此会话的已记录处理状态就会立即可用。The session state facility enables an application-defined annotation of a message session inside the broker, so that the recorded processing state relative to that session becomes instantly available when the session is acquired by a new processor.

从服务总线的角度来看,消息会话状态是一个不透明的二进制对象,可以保留一个消息大小的数据(对于服务总线标准版,大小为 256KB;对于服务总线高级版,大小为 1MB)。From the Service Bus perspective, the message session state is an opaque binary object that can hold data of the size of one message, which is 256 KB for Service Bus Standard, and 1 MB for Service Bus Premium. 相对于会话的处理状态可以保留在会话状态中,会话状态也可以指向保留此类信息的某存储位置或数据库记录。The processing state relative to a session can be held inside the session state, or the session state can point to some storage location or database record that holds such information.

用于管理会话状态的 API SetStateGetState 存在于 C# 和 Java API 的 MessageSession 对象中。The APIs for managing session state, SetState and GetState, can be found on the MessageSession object in both the C# and Java APIs. 之前没有设置会话状态的会话将对 GetState 返回空引用。A session that had previously no session state set returns a null reference for GetState. 可以使用 SetState(null) 清除之前设置的会话状态。Clearing the previously set session state is done with SetState(null).

只要不清除会话状态,会话状态将保留(返回 null),即使会话中的所有消息都已使用,也是如此。Session state remains as long as it isn't cleared up (returning null), even if all messages in a session are consumed.

可以使用 Java API 中的 SessionBrowser 方法、QueueClient 上的 GetMessageSessions 以及 .NET Framework 客户端中的 SubscriptionClient,枚举队列或订阅中的所有现有会话。All existing sessions in a queue or subscription can be enumerated with the SessionBrowser method in the Java API and with GetMessageSessions on the QueueClient and SubscriptionClient in the .NET Framework client.

队列或订阅中保留的会话状态计入相应实体的存储配额。The session state held in a queue or in a subscription counts towards that entity's storage quota. 因此,当应用程序完成会话时,建议应用程序清理保留的状态,以杜绝外部管理成本。When the application is finished with a session, it is therefore recommended for the application to clean up its retained state to avoid external management cost.

传送计数的影响Impact of delivery count

在会话上下文中,每条消息的传递计数的定义与在没有会话的情况下的定义略有不同。The definition of delivery count per message in the context of sessions varies slightly from the definition in the absence of sessions. 下表汇总了递增传送计数的时间。Here is a table summarizing when the delivery count is incremented.

方案Scenario 消息的传递计数是否递增Is the message's delivery count incremented
接受会话,但会话锁已过期(由于超时)Session is accepted, but the session lock expires (due to timeout) Yes
接受会话,会话中的消息未完成(即使它们已锁定),并且会话已关闭Session is accepted, the messages within the session aren't completed (even if they are locked), and the session is closed No
会话被接受,消息完成,然后显式关闭会话Session is accepted, messages are completed, and then the session is explicitly closed 不适用(它是标准流。N/A (It's the standard flow. 此处的消息将从会话中删除)Here messages are removed from the session)

“请求-响应”模式Request-response pattern

“请求-回复”模式是一种成熟的集成模式,它使发送方应用程序能够发送请求,并为接收程序提供了一种正确地将响应发送回发送方应用程序的方法。The request-reply pattern is a well-established integration pattern that enables the sender application to send a request and provides a way for the receiver to correctly send a response back to the sender application. 此模式通常需要一个生存期较短的队列或主题,以便应用程序向其发送响应。This pattern typically needs a short-lived queue or topic for the application to send responses to. 在此方案中,会话提供了一个具有可比较语义的简单替代解决方案。In this scenario, sessions provide a simple alternative solution with comparable semantics.

多个应用程序可以将其请求发送到单个请求队列,并将特定标头参数设置为唯一标识发送程序应用程序。Multiple applications can send their requests to a single request queue, with a specific header parameter set to uniquely identify the sender application. 接收程序应用程序可以处理传入队列的请求并在启用了会话的队列上发送答复,将会话 ID 设置为发送程序已在请求消息上发送的唯一标识符。The receiver application can process the requests coming in the queue and send replies on the session enabled queue, setting the session ID to the unique identifier the sender had sent on the request message. 然后,发送请求的应用程序可以在特定会话 ID 上接收消息,并正确地处理答复。The application that sent the request can then receive messages on the specific session ID and correctly process the replies.

备注

发送初始请求的应用程序应了解会话 ID,并使用 SessionClient.AcceptMessageSession(SessionID) 锁定需要响应的会话。The application that sends the initial requests should know about the session ID and use SessionClient.AcceptMessageSession(SessionID) to lock the session on which it's expecting the response. 一个不错的做法是使用可独一无二地将应用程序实例标识为会话 ID 的 GUID。队列上不应存在会话处理程序或 AcceptMessageSession(timeout),这是为了确保响应可由特定接收程序锁定和处理。It's a good idea to use a GUID that uniquely identifies the instance of the application as a session id. There should be no session handler or AcceptMessageSession(timeout) on the queue to ensure that responses are available to be locked and processed by specific receivers.

后续步骤Next steps

若要了解有关服务总线消息传送的详细信息,请参阅以下主题:To learn more about Service Bus messaging, see the following topics: