c# azure azureservicebus azure-eventhub

c# - ¿Qué está causando los concentradores de eventos de Azure ReceiverDisconnectedException/LeaseLostException?



azureservicebus azure-eventhub (1)

TLDR : Este comportamiento es absolutamente normal.

¿Por qué Lease Management no puede ser suave y sin excepciones ? Para dar más control sobre la situación al desarrollador.

La historia realmente larga, de EventProcessorhost básico EventProcessorhost (en este documento EPH ) es muy similar a lo que el __consumer_offset topic hace para los Kafka Consumers (propiedad de partición y tienda de punto de control) está escrita por el equipo de Microsoft Azure EventHubs - para traducir todos los EventHubs partition receiver Gu en una simple devolución de llamada onReceive(Events) .

EPH se utiliza para abordar 2 problemas generales, principales y bien conocidos al leer en secuencias de particiones de alto rendimiento como EventHubs :

  1. línea de tubería de recepción con tolerancia a fallas (por ejemplo, una versión más simple del problema) si el host que ejecuta un PartitionReceiver muere y regresa, debe reanudar el procesamiento desde donde lo dejó. Para recordar los últimos EventData procesados ​​con EventData , EPH utiliza el blob suministrado al constructor de EPH para almacenar los puntos de control, cuando el usuario invoca context.CheckpointAsync() . Eventualmente, cuando el proceso del host se detiene (por ejemplo, se reinicia repentinamente o se produce un fallo de hardware y nunca / vuelve), cualquier instancia de EPH puede realizar esta tarea y reanudarla desde ese Checkpoint .

  2. Equilibre / distribuya particiones en las instancias de EPH : digamos, si hay 10 particiones y 2 instancias de EPH procesan eventos de estas 10 particiones: necesitamos una forma de dividir las particiones en las instancias (el componente PartitionManager de la biblioteca de EPH hace esto). Utilizamos el Azure Storage - Blob LeaseManagement-feature para implementar esto. A partir de la versión 2.2.10 : para simplificar el problema, EPH asume que todas las particiones se cargan por igual .

Con esto, vamos a tratar de ver qué está pasando: Entonces, para comenzar, en el ejemplo anterior de 10 particiones de centro de eventos y 2 instancias de EPH procesan eventos fuera de ellas:

  1. Digamos que la primera instancia de EPH : EPH1 comenzó, al principio, solo y como parte de la puesta en marcha, creó receptores para las 10 particiones y está procesando eventos. En el inicio, EPH1 anunciará que posee todas estas 10 particiones al adquirir Arrendamientos en 10 blobs de almacenamiento que representan estas 10 particiones de centro de eventos (con una nomenclature estándar, que EPH crea internamente en la cuenta de Almacenamiento) desde el StorageConnectionString pasado al ctor ). Los contratos de arrendamiento se adquirirán por un tiempo determinado , después de lo cual la instancia de EPH perderá la propiedad de esta Partición.
  2. EPH1 announces continuamente de vez en cuando, que aún es propietario de esas particiones, mediante la renewing contratos de arrendamiento en el blob. La frecuencia de renewal , junto con otros ajustes útiles, se puede realizar usando PartitionManagerOptions
  3. ahora, digamos, EPH2 inicia, y usted también proporcionó el mismo AzureStorageAccount como EPH1 al ctor de EPH2 . En este momento, tiene 0 particiones para procesar. Por lo tanto, para lograr el equilibrio de las particiones en las instancias de EPH , seguirá adelante y download la lista de todos los leaseblobs que tienen la asignación de owner a partitionId . A partir de esto, STEAL arrendará los arrendamientos por su parte justa de partitions , que son 5 en nuestro ejemplo, y anunciará esa información en esa lease blob . Como parte de esto, EPH2 lee el último punto de control escrito por PartitionX , quiere robar el contrato de arrendamiento y continúa, y crea los PartitionReceiver correspondientes con el EPOCH igual al del Checkpoint .
  4. Como resultado, EPH1 perderá la propiedad de estas 5 partitions y se encontrará con diferentes errores según el estado exacto en el que se encuentre.
    • si EPH1 está invocando realmente el PartitionReceiver.Receive() call - mientras que EPH2 está creando el PartitionReceiver en el mismo receptor, EPH1 experimentará ReceiverDisconnectedException . Esto eventualmente, invocará IEventProcessor.Close(CloseReason=LeaseLost) . Tenga en cuenta que la probabilidad de alcanzar esta excepción específica es mayor si los mensajes que se reciben son mayores o si PrefetchCount es menor, ya que en ambos casos el receptor estaría realizando una E / S más agresiva.
    • si EPH1 está en el estado de checkpointing del lease o la renewing del lease , mientras que el EPH2 stole el contrato de arrendamiento, EventProcessorOptions.ExceptionReceived eventHandler se marcaría con una leaselostException (con 409 conflicto de conflicto en el leaseblob ) - que también eventualmente invoca a IEventProcess.Close(LeaseLost)

¿Por qué no puede la administración de arrendamientos ser fluida y sin excepciones ?

Para mantener al consumidor simple y sin errores, las excepciones relacionadas con la administración de arrendamientos podrían haber sido tragadas por EPH y no haber sido notificadas al código de usuario en absoluto. Sin embargo, nos dimos cuenta de que lanzar LeaseLostException podría ayudar a los clientes a encontrar errores interesantes en la devolución de llamada IEventProcessor.ProcessEvents() , para lo cual el síntoma sería: movimientos frecuentes de partición

  • interrupción menor de la red en una máquina específica - ¡debido a que EPH1 no puede renew contratos y vuelve a funcionar! - ¡e imagínese si el n / w de esta máquina permanece inestable durante un día - las instancias de EPH van a jugar ping-pong con Partitions ! Esta máquina intentará continuamente robar el contrato de arrendamiento de otra máquina, lo cual es legítimo desde el punto de vista de EPH , pero es un desastre total para el usuario de EPH , ya que interfiere completamente con la tubería de procesamiento. EPH - vería exactamente una ReceiverDisconnectedException , cuando el n / w vuelva a aparecer en este m / c escamoso! Creemos que lo mejor y de hecho la única forma es permitir que el desarrollador huela esto.
  • o un escenario simple como, tener un error en la lógica de ProcessEvents , que ProcessEvents excepciones no controladas que son fatales y ProcessEvents todo el proceso, por ejemplo, un evento venenoso. Esta partición va a moverse mucho.
  • clientes, realizando operaciones de escritura / eliminación en la misma cuenta de almacenamiento que EPH también utiliza, por error (como un script de limpieza automatizado), etc.
  • Por último, pero no menos importante, lo que nunca desearíamos que sucediera, digamos una outage 5 minutos en Azure dc donde se encuentra una EventHub.Partition específica de EventHub.Partition . Diga n / w incidente. Las particiones se moverán a través de las instancias de EPH .

Básicamente, en la mayoría de las situaciones, sería complicado, para nosotros detectar la diferencia. entre estas situaciones y un contrato de arrendamiento legítimo debido al equilibrio y queremos delegar el control de estas situaciones al Desarrollador.

Consulte el blog de nuestro PM Dan para obtener una descripción general.

Estoy recibiendo eventos de un EventHub utilizando EventProcessorHost y una clase IEventProcessor (llámelo: MyEventProcessor). Escalo a dos servidores ejecutando mi EPH en ambos servidores, y haciendo que se conecten al concentrador utilizando el mismo grupo de consumidores, pero los nombres de host únicos (utilizando el nombre de la máquina).

El problema es: en horas aleatorias del día / noche, la aplicación registra esto:

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)

Esta excepción se produce al mismo tiempo que una LeaseLostException, lanzada desde el método CloseAsync de MyEventProcessor cuando intenta un punto de control. (¿Se supone que se llama a Close debido a la ReceiverDisconnectedException?)

Creo que esto está ocurriendo debido a la gestión automática de arrendamiento de Event Hubs al escalar a múltiples máquinas. ¿Pero me pregunto si debo hacer algo diferente para que funcione de manera más limpia y evitar estas Excepciones? Ej .: ¿algo con épocas?