我遇到的情况是,我有一个asp.net核心应用程序,该应用程序在启动时注册了某个主题的订阅客户端(IHostedService
),该订阅客户端本质上具有一个回调字典,需要在任何时候触发该回调在具有ID的主题中检测到一条新消息(此ID存储在消息属性中)。该词典在应用程序的整个生命周期中都有效,并在内存中。
在azure上的asp.net核心应用程序服务的单个实例上,一切正常,一旦我扩展到2,我注意到有时订阅中的回调不会触发。这很有意义,因为我们现在有两个实例,每个实例都有自己的回调字典存储。
所以我更新了代码以检查订阅ID是否存在,如果不存在,则放弃消息,如果是,则获取回调并调用它。
public async Task HandleMessage(Microsoft.Azure.ServiceBus.Message message, CancellationToken cancellationToken)
{
var queueItem = this.converter.DeserializeItem(message);
var sessionId = // get the session id from the message
if (string.IsNullOrEmpty(sessionId))
{
await this.subscriptionClient.AbandonAsync(message.SystemProperties.LockToken);
return;
}
if (!this.subscriptions.TryGetValue(sessionId, out var subscription))
{
await this.subscriptionClient.AbandonAsync(message.SystemProperties.LockToken);
return;
}
await subscription.Call(queueItem);
// subscription was found and executed. Complete message
await this.subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
但是,问题仍然存在。我唯一的猜测是,在调用AbandonAsync
时,同一实例是否再次收到该消息?
我想我真正想问的是,如果我有一个主题订阅客户端的多个实例都指向该主题的同一订阅者,那么所有实例都可以获取该消息的副本吗?还是不能保证。
答案 0 :(得分:2)
如果我有一个主题订阅客户端的多个实例,所有这些实例都指向该主题的同一订阅者,那么所有实例都可以获得消息的副本吗?还是不能保证。
不。如果所有客户端都指向相同订阅,则只有一个接收该消息。
您遇到了与竞争对手竞争的横向扩展问题。如果要向外扩展,则永远不知道哪个实例将选择该消息。而且由于您的状态是本地的(在每个实例的内存中),所以这将不时失败。额外的缺点是成本。通过在“错误的”实例上获取消息并放弃消息,您将在消息传递方面付出更高的成本。
要解决此问题,您需要共享/集中化或更改您的体系结构。
答案 1 :(得分:0)
我设法通过使用服务总线会话来解决此问题。无论如何,我试图使用回调字典的方法基本上都是会话管理器!
服务总线会话使我可以拥有一个会话客户端的多个实例,这些实例均指向同一预订。但是,每个实例只会知道或关心它当前正在处理的会话。