12

我们在 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)。但是什么

表示当分区无法接受读取时引发的异常。

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

4

3 回答 3

15

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

  • 授予(您可以将其视为正常操作)
  • 不是主要的
  • 无写入法定人数(再次主要影响写入)
  • 重新配置待定

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

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

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

FabricNotReadableException 目前有两个已知问题。

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

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

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

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

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

于 2016-07-13T11:20:57.730 回答
0

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

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

...

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

于 2018-08-13T20:37:54.260 回答