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

我正在使用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) 

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

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

TLDR :这种行为绝对正常。

为什么租赁管理不能平稳无exception :为开发人员提供更多控制。

真正的长篇故事 – 从Basics EventProcessorhost (特此是EPH – 非常类似于__consumer_offset topicKafka Consumers做的__consumer_offset topic – 分区所有权和检查点存储)是由Microsoft Azure EventHubs团队自己编写的 – 用于翻译所有EventHubs partition receiver Gu成一个简单的onReceive(Events)回调。

在读取像EventHubs这样的高吞吐量分区流时, EPH用于解决2个一般的,主要的,众所周知的问题:

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

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

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

  1. 让我们说第一个EPH实例 – EPH1启动,单独启动,启动的一部分,它创建了所有10个分区的接收器并处理事件。 在启动时 – EPH1将宣布它拥有所有这10分区,通过获取10存储blob上的租约代表这10事件中心分区(具有标准nomenclatureEPH内部在存储帐户中创建 – 从传递给ctorStorageConnectionString )。 租约将在规定的时间内获得 ,之后EPH实例将失去该分区的所有权。
  2. EPH1不断announces – 它仍然拥有这些分区 – 通过renewing blob上的租约。 可以使用PartitionManagerOptions执行renewal频率以及其他有用的调整
  3. 现在,假设, EPH2启动 – 并且您也向EPH1ctor提供了与EPH1相同的AzureStorageAccount 。 现在,它有0分区要处理。 因此,为了实现EPH实例之间的分区平衡,它将继续download所有具有owner映射到partitionId leaseblobs的列表。 从这一点来看, STEAL租赁它的公平份额的partitions – 在我们的例子中是5 ,并将宣布该lease blob 。 作为其中的一部分, EPH2读取PartitionX编写的最新检查点,它想要窃取租约并继续创建相应的PartitionReceiver ,其EPOCHCheckpoint
  4. 因此, EPH1将失去这5个partitions所有权,并将根据其所处的确切状态遇到不同的错误。
    • 如果EPH1实际上正在调用PartitionReceiver.Receive()调用 – 当EPH2在同一个接收器上创建PartitionReceiver时 – EPH1将遇到ReceiverDisconnectedException 。 这最终会调用IEventProcessor.Close(CloseReason=LeaseLost) 。 请注意,如果接收的消息较大或PrefetchCount较小,则达到此特定exception的概率较高 – 因为在这两种情况下接收器将执行更具侵略性的I / O.
    • 如果EPH1处于checkpointing leaserenewing lease ,而EPH2 stole了租约, EventProcessorOptions.ExceptionReceived eventHandler将通过leaselostException (在leaseblob上发生409冲突错误)发出信号 – 这最终也会调用IEventProcess.Close(LeaseLost)

为什么租赁管理不能顺利和免除

为了使消费者保持简单和无差错, EPH可以吞下与租赁管理相关的exception,而根本不会通知用户代码。 然而,我们意识到,抛出LeaseLostException可以让客户在IEventProcessor.ProcessEvents()回调中找到有趣的错误 – 症状就是 – 频繁的分区移动

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

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

请参阅我们的PM Dan的博客,以获得一般概述。