预提取 Azure 服务总线消息Prefetch Azure Service Bus messages

如果在任何正式的服务总线客户端中启用了预提取,接收方会静默地获取更多消息,直至达到 PrefetchCount 上限,超出应用程序起初请求的量。When Prefetch is enabled in any of the official Service Bus clients, the receiver quietly acquires more messages, up to the PrefetchCount limit, beyond what the application initially asked for.

因此,单个初始 ReceiveReceiveAsync 调用将获取及时返回的可供立即使用的消息。A single initial Receive or ReceiveAsync call therefore acquires a message for immediate consumption that is returned as soon as available. 然后,客户端在后台获取更多信息,填充预提取缓冲区。The client then acquires further messages in the background, to fill the prefetch buffer.

启用预提取Enable prefetch

使用 .NET,通过将 MessageReceiver、QueueClient 或 SubscriptionClient 的 PrefetchCount 属性设置为大于零的数字,可启用预提取功能 。With .NET, you enable the Prefetch feature by setting the PrefetchCount property of a MessageReceiver, QueueClient, or SubscriptionClient to a number greater than zero. 将该值设为零可关闭预提取。Setting the value to zero turns off prefetch.

可轻松向 QueuesGettingStartedReceiveLoop 示例的设置的接收方添加此设置来查看这些上下文中的效果。You can easily add this setting to the receive-side of the QueuesGettingStarted or ReceiveLoop samples' settings to see the effect in those contexts.

尽管消息在预提取缓存区中提供,但后续的任何 Receive/ReceiveAsync 调用均可从缓冲区立即完成,并且随着空间变得可用,缓冲区也会在后台得到补充 。While messages are available in the prefetch buffer, any subsequent Receive/ReceiveAsync calls are immediately fulfilled from the buffer, and the buffer is replenished in the background as space becomes available. 如果没有消息用于传送,接收操作将清空缓存区,并按预期进行等待或阻止。If there are no messages available for delivery, the receive operation empties the buffer and then waits or blocks, as expected.

预提取也同样适用于 OnMessageOnMessageAsync API。Prefetch also works in the same way with the OnMessage and OnMessageAsync APIs.

既然预提取更快,为何不是默认选项?If it is faster, why is Prefetch not the default option?

预提取可加快消息流程,方法是在应用程序请求消息时及请求消息前,准备好消息用于本地检索。Prefetch speeds up the message flow by having a message readily available for local retrieval when and before the application asks for one. 这种吞吐量提升是应用程序作者不得不明确作出的某种权衡的结果:This throughput gain is the result of a trade-off that the application author must make explicitly:

通过 ReceiveAndDelete 接收模式,预提取缓存区获取的所有消息在队列中不再可用,仅驻留在内存中预提取缓存区,直到应用程序通过 Receive/ReceiveAsync 或 OnMessage/OnMessageAsync API 接收到它们 。With the ReceiveAndDelete receive mode, all messages that are acquired into the prefetch buffer are no longer available in the queue, and only reside in the in-memory prefetch buffer until they are received into the application through the Receive/ReceiveAsync or OnMessage/OnMessageAsync APIs. 如果在应用程序接收到消息前终止应用程序,这些消息将丢失,且不可恢复。If the application terminates before the messages are received into the application, those messages are irrecoverably lost.

PeekLock 接收模式下,提取到预提取缓存区的消息将以锁定状态进入缓存区,并且将超时时钟用于锁定计时。In the PeekLock receive mode, messages fetched into the Prefetch buffer are acquired into the buffer in a locked state, and have the timeout clock for the lock ticking. 如果预提取缓存区很大,且处理所需时间过长,以致消息锁定在驻留于预提取缓存区,甚至应用程序还在处理消息时就到期,可能出现一些令人困惑的事件要应用程序处理。If the prefetch buffer is large, and processing takes so long that message locks expire while residing in the prefetch buffer or even while the application is processing the message, there might be some confusing events for the application to handle.

应用程序可能收到包含到期或即将到期的锁定的消息。The application might acquire a message with an expired or imminently expiring lock. 如果是这样,应用程序可能处理该消息,但随后发现,因锁定到期而无法完成处理。If so, the application might process the message, but then find that it cannot complete it due to a lock expiration. 应用程序可查看 LockedUntilUtc 属性(受代理时钟和本地计算机时钟之间的时钟偏差约束)。The application can check the LockedUntilUtc property (which is subject to clock skew between the broker and local machine clock). 如果消息锁定已到期,则应用程序必须忽略该消息,不应对该消息或通过该消息调用任何 API。If the message lock has expired, the application must ignore the message; no API call on or with the message should be made. 如果消息未到期但即将到期,可通过调用 message.RenewLock() 延续和扩展又一默认锁定时间段If the message is not expired but expiration is imminent, the lock can be renewed and extended by another default lock period by calling message.RenewLock()

如果锁定在预提取缓冲区静默地到期,则视为已放弃该消息,且可再次将消息用于从队列进行检索。If the lock silently expires in the prefetch buffer, the message is treated as abandoned and is again made available for retrieval from the queue. 这可能导致将消息提取到预提取缓冲区,并置于末尾。That might cause it to be fetched into the prefetch buffer; placed at the end. 如果在消息过期期间往往无法使用预提取缓存区,这将导致重复预提取消息,但始终无法将其以可用(有效锁定)状态有效送达,并最终在超出最大传送数后移动到死信队列。If the prefetch buffer cannot usually be worked through during the message expiration, this causes messages to be repeatedly prefetched but never effectively delivered in a usable (validly locked) state, and are eventually moved to the dead-letter queue once the maximum delivery count is exceeded.

如果消息处理需要高度的可靠性,且处理需要大量精力和时间,则建议谨慎使用或者丝毫不用预提取功能。If you need a high degree of reliability for message processing, and processing takes significant work and time, it is recommended that you use the prefetch feature conservatively, or not at all.

如果需要较高吞吐量且消息处理通常比较便宜,则预提取会产生显著的吞吐量优势。If you need high throughput and message processing is commonly cheap, prefetch yields significant throughput benefits.

需要均衡对队列或订阅配置的最大预提取数和锁定持续时间,以便锁定超时至少超出最大预提取缓存区大小外加一条消息的累积预期消息处理时间。The maximum prefetch count and the lock duration configured on the queue or subscription need to be balanced such that the lock timeout at least exceeds the cumulative expected message processing time for the maximum size of the prefetch buffer, plus one message. 同时,锁定超时不应过长,防止消息在被意外丢弃后超出其最大 TimeToLive,因此需要消息的锁定在重新传送消息前到期。At the same time, the lock timeout ought not to be so long that messages can exceed their maximum TimeToLive when they are accidentally dropped, thus requiring their lock to expire before being redelivered.

后续步骤Next steps

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