是什么导致Azure事件中心ReceiverDisconnectedException / LeaseLostException?

时间:2017-01-05 23:52:40

标签: c# azure azureservicebus azure-eventhub

我正在使用EventProcessorHost和一个IEventProcessor类(调用它:MyEventProcessor)从EventHub接收事件。我通过在两台服务器上运行我的EPH,并使用相同的ConsumerGroup连接到Hub,但使用唯一的hostName(使用机器名称)将其扩展到两台服务器。

问题是:在白天/黑夜的随机时间,应用程序记录下来:

Exception information: 
Exception type: ReceiverDisconnectedException 
Exception message: New receiver with higher epoch of '186' is created hence current receiver with epoch '186' is getting disconnected. If you are recreating the receiver, make sure a higher epoch is used.
  at Microsoft.ServiceBus.Common.ExceptionDispatcher.Throw(Exception exception)
  at Microsoft.ServiceBus.Common.Parallel.TaskHelpers.EndAsyncResult(IAsyncResult asyncResult)
  at Microsoft.ServiceBus.Messaging.IteratorAsyncResult`1.StepCallback(IAsyncResult result)

此异常与LeaseLostException同时发生,当它尝试检查点时,从MyEventProcessor的CloseAsync方法抛出。 (由于ReceiverDisconnectedException,可能正在调用Close?)

我认为这是由于Event Hubs在扩展到多台计算机时的自动租赁管理而发生的。但我想知道我是否需要做一些不同的事情以使其更干净地工作并避免这些例外?例如:有时代的东西?

1 个答案:

答案 0 :(得分:37)

TLDR :这种行为绝对正常。

为什么租赁管理不能畅通无阻;无异常:为开发人员提供更多控制。

非常漫长的故事 - 从Basics的全程 EventProcessorhost(特此EPH - 非常类似于__consumer_offset topicKafka Consumers所做的事情 - 分区所有权和检查点商店)由Microsoft Azure EventHubs团队自己编写 - 将所有EventHubs partition receiver Gu转换为简单的onReceive(Events)回调。

EPH用于解决2个一般性的,主要的,众所周知的问题,同时读取高吞吐量的分区流,如EventHubs

  1. 容错接收管道 - 例如:问题的更简单版本 - 如果运行PartitionReceiver的主机死亡并返回 - 它需要恢复处理从它离开的地方。要记住上次成功处理的EventDataEPH使用提供给blob构造函数的EPH来存储检查点 - 当用户调用context.CheckpointAsync()时。最终,当主机进程终止时(例如:突然重新启动或命中硬件故障并且永远不会/恢复) - 任何EPH实例都可以接收此任务并从Checkpoint恢复。

  2. EPH个实例之间平衡/分配分区 - 假设有10个分区和2个EPH实例处理来自这10个分区的事件 - 我们需要一种跨实例划分分区的方法(PartitionManager库的EPH组件执行此操作)。我们使用 Azure Storage - Blob LeaseManagement-feature 来实现此目的。自版本2.2.10起 - 为了简化问题,EPH假定所有分区均等地加载

  3. 有了这个,让我们试着看看发生了什么: 因此,首先,在10事件中心分区和2 EPH个实例的上述示例中处理事件:

    1. 假设第一个EPH实例 - EPH1开始,首先,单独启动,启动的一部分,它为所有10个分区创建接收器并处理事件。在启动时 - EPH1将通过获取代表这些10事件中心分区的10存储blob上的租约来宣布它拥有所有这些10分区(具有标准{{ 1}} - nomenclature内部在存储帐户中创建的内容 - 从传递给EPH的{​​{1}}开始。租约将为acquired for a set time,之后StorageConnectionString实例将失去此分区的所有权。
    2. ctor偶尔会EPH - 它仍然拥有这些分区 - 通过EPH1租约在blob上。可以使用PartitionManagerOptions
    3. 执行announces的频率以及其他有用的调整
    4. 现在,让我们说,renewing启动了 - 您也提供了与renewal EPH2相同的AzureStorageAccount EPH1。现在,它有ctor个分区要处理。因此,要在EPH2个实例之间实现分区的平衡,它将继续0所有EPH的列表,其downloadleaseblobs的映射。在此基础上, owner租约以获得partitionId的公平份额 - 在我们的示例中为STEAL,并将宣布该{{1}的信息}}。作为其中的一部分,partitions读取由5写的最新检查点,它想要窃取租约并继续使用{{1}创建相应的lease blob。与EPH2中的那个相同。
    5. 因此,PartitionX将失去对这5个PartitionReceiver的所有权,并会根据其所处的确切状态遇到不同的错误。
      • 如果EPOCH实际上正在调用Checkpoint来电,而EPH1正在同一接收方创建partitions,则EPH1会遇到ReceiverDisconnectedException PartitionReceiver.Receive() }。这最终会调用EPH2。请注意,如果接收的消息较大或PartitionReceiver较小,则触及此特定异常的概率较高 - 因为在这两种情况下接收器将执行更具侵略性的I / O.
      • 如果EPH1状态为IEventProcessor.Close(CloseReason=LeaseLost) PrefetchCountEPH1 checkpointinglease renewing在租约中,lease eventHandler会通过EPH2stole上发生EventProcessorOptions.ExceptionReceived冲突错误)发出信号 - 最终也会调用leaselostException
    6. 为什么租赁管理不能畅通无阻;异常无

      为了使消费者保持简单和无差错,409可能会吞下与租约管理相关的异常,而根本不会通知用户代码。但是,我们意识到,抛出leaseblob可以让客户在IEventProcess.Close(LeaseLost)回调中找到有趣的错误 - 症状就是 - 频繁的分区移动

      • 特定计算机上的小型网络中断 - 由于EPH未能LeaseLostException租约而重新启动! - 并且想象一下,如果这台机器的n / w在一天内显得不稳定 - IEventProcessor.ProcessEvents()个实例将与EPH1一起玩renew!这台机器将不断尝试从其他机器窃取租约 - 从EPH观点来看这是合法的 - 但是,对于ping-pong的用户来说,这是完全的灾难 - 因为它完全干扰了加工管道。 Partitions - 当n / w重新回到这个片状m / c时,会看到EPH!我们认为最好也是最好的方法是让开发人员闻到这一点!
      • 或者一个简单的场景,例如,EPH逻辑中的错误 - 抛出未处理的异常,这些异常是致命的并且会导致整个过程 - 例如:毒害事件。这个分区将会移动很多。
      • 客户,在EPH正在使用的同一存储帐户上执行写入/删除操作 - 错误(如自动清理脚本)等。
      • 最后但并非最不重要 - 我们希望不会发生这种情况 - 比如在Azure d.c上有一个5分钟ReceiverDisconnectedException,其中有一个特定的ProcessEvents所在位置 - 比如n / w事件。分区将在EPH个实例中移动。

      基本上,在大多数情况下,对我们来说检测差异会很棘手。在这些情况和合法租约之间由于平衡导致丢失,我们希望将这些情况的控制委托给开发人员。

      more on Event Hubs...