我正在使用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在扩展到多台计算机时的自动租赁管理而发生的。但我想知道我是否需要做一些不同的事情以使其更干净地工作并避免这些例外?例如:有时代的东西?
答案 0 :(得分:37)
TLDR :这种行为绝对正常。
为什么租赁管理不能畅通无阻;无异常:为开发人员提供更多控制。
非常漫长的故事 - 从Basics的全程
EventProcessorhost
(特此EPH
- 非常类似于__consumer_offset topic
对Kafka Consumers
所做的事情 - 分区所有权和检查点商店)由Microsoft Azure EventHubs
团队自己编写 - 将所有EventHubs partition receiver Gu
转换为简单的onReceive(Events)
回调。
EPH
用于解决2个一般性的,主要的,众所周知的问题,同时读取高吞吐量的分区流,如EventHubs
:
容错接收管道 - 例如:问题的更简单版本 - 如果运行PartitionReceiver
的主机死亡并返回 - 它需要恢复处理从它离开的地方。要记住上次成功处理的EventData
,EPH
使用提供给blob
构造函数的EPH
来存储检查点 - 当用户调用context.CheckpointAsync()
时。最终,当主机进程终止时(例如:突然重新启动或命中硬件故障并且永远不会/恢复) - 任何EPH
实例都可以接收此任务并从Checkpoint
恢复。
在EPH
个实例之间平衡/分配分区 - 假设有10个分区和2个EPH
实例处理来自这10个分区的事件 - 我们需要一种跨实例划分分区的方法(PartitionManager
库的EPH
组件执行此操作)。我们使用 Azure Storage - Blob LeaseManagement-feature
来实现此目的。自版本2.2.10
起 - 为了简化问题,EPH
假定所有分区均等地加载。
有了这个,让我们试着看看发生了什么:
因此,首先,在10
事件中心分区和2
EPH
个实例的上述示例中处理事件:
EPH
实例 - EPH1
开始,首先,单独启动,启动的一部分,它为所有10个分区创建接收器并处理事件。在启动时 - EPH1
将通过获取代表这些10
事件中心分区的10
存储blob上的租约来宣布它拥有所有这些10
分区(具有标准{{ 1}} - nomenclature
内部在存储帐户中创建的内容 - 从传递给EPH
的{{1}}开始。租约将为acquired for a set time,之后StorageConnectionString
实例将失去此分区的所有权。ctor
偶尔会EPH
- 它仍然拥有这些分区 - 通过EPH1
租约在blob上。可以使用PartitionManagerOptions
announces
的频率以及其他有用的调整
renewing
启动了 - 您也提供了与renewal
EPH2
相同的AzureStorageAccount
EPH1
。现在,它有ctor
个分区要处理。因此,要在EPH2
个实例之间实现分区的平衡,它将继续0
所有EPH
的列表,其download
到leaseblobs
的映射。在此基础上, owner
租约以获得partitionId
的公平份额 - 在我们的示例中为STEAL
,并将宣布该{{1}的信息}}。作为其中的一部分,partitions
读取由5
写的最新检查点,它想要窃取租约并继续使用{{1}创建相应的lease blob
。与EPH2
中的那个相同。 PartitionX
将失去对这5个PartitionReceiver
的所有权,并会根据其所处的确切状态遇到不同的错误。
EPOCH
实际上正在调用Checkpoint
来电,而EPH1
正在同一接收方创建partitions
,则EPH1
会遇到ReceiverDisconnectedException PartitionReceiver.Receive()
}。这最终会调用EPH2
。请注意,如果接收的消息较大或PartitionReceiver
较小,则触及此特定异常的概率较高 - 因为在这两种情况下接收器将执行更具侵略性的I / O. EPH1
状态为IEventProcessor.Close(CloseReason=LeaseLost)
PrefetchCount
或EPH1
checkpointing
,lease
renewing
在租约中,lease
eventHandler会通过EPH2
(stole
上发生EventProcessorOptions.ExceptionReceived
冲突错误)发出信号 - 最终也会调用leaselostException
。
为什么租赁管理不能畅通无阻;异常无强>:
为了使消费者保持简单和无差错,409
可能会吞下与租约管理相关的异常,而根本不会通知用户代码。但是,我们意识到,抛出leaseblob
可以让客户在IEventProcess.Close(LeaseLost)
回调中找到有趣的错误 - 症状就是 - 频繁的分区移动
EPH
未能LeaseLostException
租约而重新启动! - 并且想象一下,如果这台机器的n / w在一天内显得不稳定 - IEventProcessor.ProcessEvents()
个实例将与EPH1
一起玩renew
!这台机器将不断尝试从其他机器窃取租约 - 从EPH
观点来看这是合法的 - 但是,对于ping-pong
的用户来说,这是完全的灾难 - 因为它完全干扰了加工管道。 Partitions
- 当n / w重新回到这个片状m / c时,会看到EPH
!我们认为最好也是最好的方法是让开发人员闻到这一点!EPH
逻辑中的错误 - 抛出未处理的异常,这些异常是致命的并且会导致整个过程 - 例如:毒害事件。这个分区将会移动很多。EPH
正在使用的同一存储帐户上执行写入/删除操作 - 错误(如自动清理脚本)等。ReceiverDisconnectedException
,其中有一个特定的ProcessEvents
所在位置 - 比如n / w事件。分区将在EPH
个实例中移动。
基本上,在大多数情况下,对我们来说检测差异会很棘手。在这些情况和合法租约之间由于平衡导致丢失,我们希望将这些情况的控制委托给开发人员。