FabricNotReadableException 是什么意思?我们应该如何应对?

What does the FabricNotReadableException mean? And how should we respond to it?

我们在 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 link (https://msdn.microsoft.com/en-us/library/azure/system.fabric.fabricnotreadableexception.aspx)。但是

是什么意思

Represents an exception that is thrown when a partition cannot accept reads.

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

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

  • 授予(您可以认为这是正常操作)
  • 不是小学
  • 无写入仲裁(再次主要影响写入)
  • 重新配置待定

每当尝试对当前不是主副本并映射到 NotPrimary 状态的副本进行写入时,都会抛出您提到的 FabricNotPrimaryException。

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

您可以考虑 FabricNotReadableException 可重试。如果您看到它,只需再次尝试调用,最终它将解析为 NotPrimary 或 Granted。如果你得到 FabricNotPrimary 异常,通常这应该被抛回给客户端(或以某种方式通知客户端)它需要重新解析以找到当前的主(Service Fabric 提供的默认通信堆栈)监视不可重试的异常并代表您重新解决)。

FabricNotReadableException 目前有两个已知问题。

  1. FabricNotReadableException 应该有两个变体。第一个应该是显式可重试的 (FabricTransientNotReadableException),第二个应该是 FabricNotReadableException。第一个版本 (Transient) 是最常见的,可能是您 运行 正在进入的版本,当然在大多数情况下您会 运行 进入。第二个(非瞬态)将在您最终与备用副本对话的情况下返回。开箱即用的传输和重试逻辑不会与备用数据库通信,但如果您有自己的逻辑,则可以 运行 加入其中。
  2. 另一个问题是,今天 FabricNotReadableException 应该派生自 FabricTransientException,这样更容易确定正确的行为是什么。

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

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

我的服务中的

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

如果 ICommunicationClient 有一个 RunAsync 接口,该接口在 Fabric 变为 ready/readable 时调用并在 Fabric 关闭副本时取消 - 这将大大简化我的生活。 :)

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

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 ();

...

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