Je reçois des événements d'un EventHub utilisant EventProcessorHost et une classe IEventProcessor (appelez-le: MyEventProcessor). J'élargis cela à deux serveurs en exécutant mon EPH sur les deux serveurs et en les faisant se connecter au concentrateur en utilisant le même ConsumerGroup, mais un nom d'hôte unique (en utilisant le nom de la machine).
Le problème est: à des heures aléatoires du jour/de la nuit, l'application enregistre ceci:
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)
Cette exception se produit en même temps qu'une LeaseLostException, levée à partir de la méthode CloseAsync de MyEventProcessor lorsqu'il essaie de vérifier. (On peut supposer que Close est appelé en raison de ReceiverDisconnectedException?)
Je pense que cela se produit en raison de la gestion automatique des baux d'Event Hubs lors de la mise à l'échelle sur plusieurs machines. Mais je me demande si je dois faire quelque chose de différent pour le faire fonctionner plus proprement et éviter ces exceptions? Par exemple: quelque chose avec des époques?
[~ # ~] tldr [~ # ~] : Ce comportement est absolument normal.
Pourquoi la gestion des baux ne peut-elle pas être fluide et sans exception : Pour donner plus de contrôle sur la situation au développeur.
La très longue histoire - tout le chemin depuis les bases EventProcessorhost
(ici EPH
- est très similaire à ce que __consumer_offset topic
Fait pour Kafka Consumers
- propriété de la partition et magasin de points de contrôle) est écrit par l'équipe de Microsoft Azure EventHubs
Elle-même - pour traduire tous les EventHubs partition receiver Gu
En une simple fonction onReceive(Events)
rappeler.
EPH
est utilisé pour résoudre 2 problèmes généraux, majeurs et bien connus lors de la lecture d'un flux partitionné à haut débit comme EventHubs
:
pipe-line de réception tolérant aux pannes - par exemple: une version plus simple du problème - si l'hôte exécutant un PartitionReceiver
meurt et revient - il doit reprendre le traitement là où il est parti. Pour se souvenir du dernier traitement réussi de EventData
, EPH
utilise le blob
fourni au constructeur EPH
pour stocker les points de contrôle - chaque fois que l'utilisateur invoque context.CheckpointAsync()
. Finalement, lorsque le processus hôte meurt (par exemple, redémarre brusquement ou frappe une défaillance matérielle et ne revient jamais) - n'importe quelle instance EPH
peut reprendre cette tâche et reprendre à partir de cette Checkpoint
.
Équilibrer/distribuer des partitions sur EPH
instances - disons s'il y a 10 partitions et 2 EPH
instances traitant des événements à partir de ces 10 partitions - nous avons besoin d'un moyen de diviser les partitions entre les instances (le composant PartitionManager
de la bibliothèque EPH
le fait). Nous utilisons Azure Storage - Blob LeaseManagement-feature
pour implémenter cela. Depuis la version 2.2.10
- pour simplifier le problème, EPH
suppose que toutes les partitions sont chargées de manière égale .
Avec cela, essayons de voir ce qui se passe: Donc, pour commencer, dans l'exemple ci-dessus de partitions de concentrateur d'événements 10
Et d'instances 2
EPH
traitant des événements à partir de celles-ci :
EPH
- EPH1
a démarré, au début, seule et une partie du démarrage, elle a créé des récepteurs pour les 10 partitions et traite les événements. Au démarrage - EPH1
Annoncera qu'il possède toutes ces partitions 10
En acquérant des baux sur des objets de stockage 10
Représentant ces partitions de concentrateur d'événements 10
(Avec un nomenclature
standard - que EPH
crée en interne dans le compte de stockage - du StorageConnectionString
transmis au ctor
). Les baux seront acquis pour une durée définie , après quoi l'instance EPH
perdra la propriété de cette partition.EPH1
Continuellement announces
de temps en temps - qu'il est toujours propriétaire de ces partitions - par renewing
baux sur le blob. La fréquence de renewal
, ainsi que d'autres réglages utiles, peuvent être effectués en utilisant PartitionManagerOptions
EPH2
démarre - et vous avez également fourni le même AzureStorageAccount
que EPH1
au ctor
de EPH2
. À l'heure actuelle, il a des partitions 0
À traiter. Ainsi, pour atteindre l'équilibre des partitions entre les instances de EPH
, il ira de l'avant et download
la liste de tous leaseblobs
qui a le mappage de owner
vers partitionId
. À partir de cela, il louera STEAL
pour sa juste part de partitions
- qui est 5
dans notre exemple, et annoncera cette information sur ce lease blob
. Dans ce cadre, EPH2
Lit le dernier point de contrôle écrit par PartitionX
pour lequel il veut voler le bail et va de l'avant et crée les PartitionReceiver
correspondants avec le Epoch
identique à celui du Checkpoint
.EPH1
Perdra la propriété de ces 5 partitions
et se heurtera à différentes erreurs en fonction de l'état exact dans lequel il se trouve. EPH1
invoque réellement l'appel PartitionReceiver.Receive()
- tandis que EPH2
crée le PartitionReceiver
sur le même récepteur - EPH1
connaîtra - ReceiverDisconnectedException . Cela finira par appeler IEventProcessor.Close(CloseReason=LeaseLost)
. Notez que la probabilité de toucher cette exception spécifique est plus élevée si les messages reçus sont plus gros ou si PrefetchCount
est plus petit - comme dans les deux cas, le récepteur effectuerait des E/S plus agressives.EPH1
est dans l'état de checkpointing
le lease
ou renewing
le lease
, tandis que le EPH2
stole
le bail, le gestionnaire d'événements EventProcessorOptions.ExceptionReceived
Serait signalé avec une erreur de conflit leaselostException
(avec 409
Sur le leaseblob
) - qui finit également par être invoquée IEventProcess.Close(LeaseLost)
.Pourquoi la gestion des baux ne peut-elle pas être fluide et sans exception :
Pour que le consommateur reste simple et sans erreur, les exceptions liées à la gestion des baux auraient pu être avalées par EPH
et non notifiées du tout au code utilisateur. Cependant, nous avons réalisé que lancer LeaseLostException
pourrait permettre aux clients de trouver des bogues intéressants dans la fonction de rappel IEventProcessor.ProcessEvents()
- pour lequel le symptôme serait - des mouvements de partition fréquents
EPH1
ne parvient pas à renew
baux et revient! - et imaginez si le n/w de cette machine est instable pendant un jour - EPH
les instances vont jouer ping-pong
avec Partitions
! Cette machine essaiera continuellement de voler le bail d'une autre machine - ce qui est légitime du point de vue EPH
- mais, c'est un désastre total pour l'utilisateur de EPH
- car il interfère complètement avec le tuyau de traitement. EPH
- verrait exactement un ReceiverDisconnectedException
, lorsque le n/w revient sur ce m/c feuilleté! Nous pensons que le meilleur et en fait le seul moyen est de permettre au développeur de sentir cela!ProcessEvents
- qui lève des exceptions non gérées qui sont fatales et fait tomber tout le processus - ex: un événement empoisonné. Cette partition va beaucoup bouger.EPH
utilise également - par erreur (comme un script de nettoyage automatisé), etc.outage
sur Azure d.c où se trouve un EventHub.Partition
spécifique - disons un incident n/w. Les partitions vont se déplacer entre les instances de EPH
.Fondamentalement, dans la majorité des situations, il serait difficile - pour nous de détecter le diff. entre ces situations et un bail légitime perdu en raison de l'équilibrage et nous voulons déléguer le contrôle de ces situations au développeur.