FabricNotReadableException 是啥意思?以及我们应该如何应对?

Posted

技术标签:

【中文标题】FabricNotReadableException 是啥意思?以及我们应该如何应对?【英文标题】:What does the FabricNotReadableException mean? And how should we respond to it?FabricNotReadableException 是什么意思?以及我们应该如何应对? 【发布时间】:2016-03-01 20:07:41 【问题描述】:

我们在 Service-Fabric 上的有状态服务中使用以下方法。该服务具有分区。有时我们会从这段代码中得到 FabricNotReadableException。

public async Task HandleEvent(EventHandlerMessage message)

    var queue = await StateManager.GetOrAddAsync<IReliableQueue<EventHandlerMessage>>(EventHandlerServiceConstants.EventHandlerQueueName);
    using(ITransaction tx = StateManager.CreateTransaction())
    
      await queue.EnqueueAsync(tx, message);
      await tx.CommitAsync();
    

这是否意味着分区已关闭并正在移动?其中我们遇到了二级分区?因为在某些情况下还会引发 FabricNotPrimaryException。

我看到了 MSDN 链接 (https://msdn.microsoft.com/en-us/library/azure/system.fabric.fabricnotreadableexception.aspx)。但是

表示当分区不能接受读取时抛出的异常。

是什么意思?分区不能接受读取是怎么回事?

【问题讨论】:

msdn.microsoft.com/en-us/library/azure/…Google 是你的朋友 @TheLethalCoder 并没有让它更清楚:( 【参考方案1】:

在幕后,Service Fabric 有几个状态会影响给定副本是否可以安全地提供读取和写入服务。它们是:

允许(您可以将其视为正常操作) 不是主要的 无写入仲裁(再次主要影响写入) 重新配置待定

您提到的FabricNotPrimaryException 可以在尝试对当前不是主要的副本进行写入并映射到NotPrimary 状态时抛出。

FabricNotReadableException 映射到其他状态(您实际上不需要担心或区分它们),并且可能在多种情况下发生。一个示例是,如果您尝试执行读取的副本是“备用”副本(已关闭并已恢复的副本,但副本集中已经有足够的活动副本)。另一个示例是,如果副本是主副本,但正在关闭(例如由于升级或报告故障),或者它当前正在重新配置(例如,正在添加另一个副本)。由于 Service Fabric 需要在后台处理某些安全检查和原子更改,所有这些条件都将导致副本在短时间内无法满足写入操作。

您可以考虑 FabricNotReadableException 可重试。如果您看到它,只需再次尝试调用,最终它将解析为 NotPrimary 或 Granted。如果您收到 FabricNotPrimary 异常,通常应该将其返回给客户端(或以某种方式通知客户端),它需要重新解析才能找到当前的 Primary(Service Fabric 提供的默认通信堆栈负责处理)查看不可重试的异常并代表您重新解决)。

FabricNotReadableException 目前存在两个已知问题。

    FabricNotReadableException 应该有两个变体。第一个应该是显式可重试的 (FabricTransientNotReadableException),第二个应该是 FabricNotReadableException。第一个版本(瞬态)是最常见的,可能是您遇到的,当然在大多数情况下您会遇到的。在您最终与备用副本交谈的情况下,将返回第二个(非瞬态)。使用开箱即用的传输和重试逻辑不会与待机对话,但如果您有自己的逻辑,则可能会遇到它。 另一个问题是今天 FabricNotReadableException 应该派生自 FabricTransientException,从而更容易确定正确的行为是什么。

【讨论】:

Matt,看来(在我的开发集群上),我经常遇到这个异常,因为我有状态可靠的服务。我正在我的 CommunicationListener 中访问以下方法 StateManager.GetOrAddAsync>("name") 并始终获得 FabricNotReadableException ...知道如何解决它吗? 这可能是因为我的服务生命周期还太早。我在我的通信侦听器的 OpenAsync 中执行 GetOrAddAsync 方法,在那里我得到了异常。如果我在 RunAsync 中执行此操作,则一切正常。现在的问题是:如何安全地访问通信监听器中的 ReliableCollections? OpenAsync 应该只是建立侦听器然后完成 - 当时该服务不可读/可写。期望是请求稍后进入(到从 Open 返回的地址),此时它们被处理。在副本可读之前,我们不会启动 RunAsync,这就是您在那里看不到错误的原因。【参考方案2】:

作为答案发布(对 asnider 的评论 - 3 月 16 日 17:42),因为它对于 cmets 来说太长了! :)

我也陷入了这个 catch 22。我的 svc 启动并立即接收消息。我想将服务启动封装在 OpenAsync 中并设置一些 ReliableDictionary 值,然后开始接收消息。但是,此时 Fabric 不可读,我需要在 OpenAsync 和 RunAsync 之间拆分这个“启动”:(

我的服务中的RunAsync 和我的客户端中的OpenAsync 似乎也有不同的取消令牌,所以我也需要解决如何处理这个问题。只是感觉有点乱。我有很多关于如何在我的代码中整理它的想法,但是有没有人想出一个优雅的解决方案?

如果ICommunicationClient 有一个 RunAsync 接口,该接口在 Fabric 准备好/可读时被调用并在 Fabric 关闭副本时被取消 - 这将大大简化我的生活。 :)

【讨论】:

明白了,但是很遗憾,因为读/写状态随时都可能被带走,所以这总是一场竞赛。因此,即使 API 仅在一切准备就绪时才被调用,到服务实际响应时,写入状态可能会有所不同。一般来说,如果由于某种原因设置未完成,人们应该开始处理请求并使其失败,并依赖客户端重试。 我最终使用 Polly 和一些抽象来使其可靠(请原谅双关语)。 github.com/App-vNext/Polly【参考方案3】:

我遇到了同样的问题。我的侦听器在服务的主线程之前启动。我将需要启动的侦听器列表排队,然后在主线程中尽早激活它们。结果,所有传入的消息都能够被处理并放入适当的可靠存储中。我的简单解决方案(这是一个服务总线监听器):

public Task<string> OpenAsync (CancellationToken cancellationToken)

  string uri;

  Start ();
  uri = "<your endpoint here>";
  return Task.FromResult (uri);


public static object lockOperations = new object ();
public static bool operationsStarted = false;
public static List<ClientAuthorizationBusCommunicationListener> pendingStarts = new List<ClientAuthorizationBusCommunicationListener> ();
public static void StartOperations ()

  lock (lockOperations)
  
    if (!operationsStarted)
    
      foreach (ClientAuthorizationBusCommunicationListener listener in pendingStarts)
      
        listener.DoStart ();
      
      operationsStarted = true;
    
  


private static void QueueStart (ClientAuthorizationBusCommunicationListener listener)

  lock (lockOperations)
  
    if (operationsStarted)
    
      listener.DoStart ();
    
    else
    
      pendingStarts.Add (listener);
    
  


private void Start ()

  QueueStart (this);


private void DoStart ()

  ServiceBus.WatchStatusChanges (HandleStatusMessage,
    this.clientId,
    out this.subscription);

==========================

在主线程中,调用函数启动监听操作:

protected override async Task RunAsync (CancellationToken cancellationToken)

  ClientAuthorizationBusCommunicationListener.StartOperations ();

...

这个问题可能在这里表现出来,因为有问题的总线已经有消息,并在创建侦听器的第二秒开始触发。尝试访问状态管理器中的任何内容都会引发您所询问的异常。

【讨论】:

以上是关于FabricNotReadableException 是啥意思?以及我们应该如何应对?的主要内容,如果未能解决你的问题,请参考以下文章