【问题标题】:Azure service bus multiple instances for the same subscriber同一订阅者的 Azure 服务总线多个实例
【发布时间】:2019-03-21 08:50:51
【问题描述】:

我有一个 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 时,同一个实例又拿起了消息?

我想我真正想问的是,如果我有多个主题订阅客户端实例都指向该主题的同一个订阅者,是否所有实例都可以获得消息的副本?还是不能保证。

【问题讨论】:

    标签: asp.net-core azureservicebus azure-servicebus-topics


    【解决方案1】:

    如果我有多个主题订阅客户端实例都指向该主题的同一个订阅者,是否所有实例都可以获得消息的副本?还是不能保证。

    没有。如果所有客户端都指向相同的订阅,则只有一个会收到该消息。

    您遇到了与竞争消费者进行横向扩展的问题。如果您要向外扩展,您永远不知道哪个实例会选择消息。而且由于您的状态是本地的(在每个实例的内存中),这将不时失败。额外的缺点是成本。通过在“错误”实例上获取消息并放弃,您将在消息传递方面付出更高的成本。

    要解决此问题,您需要共享/集中式或围绕此更改您的架构。

    【讨论】:

    • 确实如此。我刚刚注意到微软在服务总线的 dotnet 核心版本中包含了管理 API 的子集,在这种情况下,我将在新节点启动时动态创建订阅:)
    • 另外,服务总线会话可以用于我的事业吗?
    • 唯一的缺点是每个会话没有横向扩展,因为只有一个消费者可以处理给定的会话消息。
    • 我认为这对我的事业来说没问题。因为我的会议时间很短。感谢您的所有建议!
    • @SeanFeldman 我正在使用这种方式,您将其描述为预期的流程。我有 2 个实例连接到相同的订阅名称,只有 1 个收到消息。我的问题是我需要断开一个实例,以便另一个实例开始接收所有消息。我在从 SubscriptionClient.CreateFromConnectionString 返回的 subscriptionClient 上调用 subscription.Close(),但即使在我调用它之后,我仍然没有收到剩余实例上的每条消息。你知道有什么方法可以完全断开一个实例,让它表现得好像该实例从未存在过
    【解决方案2】:

    我设法通过使用服务总线会话解决了这个问题。无论如何,我试图用回调字典做的基本上是一个会话管理器!

    服务总线会话允许我有多个会话客户端实例都指向同一个订阅。但是,每个实例只会知道或关心它当前正在处理的会话。

    【讨论】:

      猜你喜欢
      • 2020-07-06
      • 1970-01-01
      • 1970-01-01
      • 2017-10-23
      • 2014-02-07
      • 1970-01-01
      • 2020-06-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多