Подтвердить что ты не робот

Что вызывает Azure Event Hubs ReceiverDisconnectedException/LeaseLostException?

Я получаю события от EventHub, используя EventProcessorHost и класс IEventProcessor (называйте это: MyEventProcessor). Я масштабирую это на двух серверах, запустив свой EPH на обоих серверах и связав их с концентратором, используя одну и ту же потребительскую группу, но уникальное имя узла (с использованием имени машины).

Проблема: в случайные часы дня и ночи приложение регистрирует это:

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 при попытке контрольной точки. (Предположительно Close вызывается из-за исключения ReceiverDisconnectedException?)

Я думаю, что это происходит из-за автоматического управления арендой Event Hubs при масштабировании на несколько компьютеров. Но мне интересно, нужно ли мне делать что-то другое, чтобы заставить его работать более чисто и избегать этих Исключений? Например: что-то с эпохами?

4b9b3361

Ответ 1

TLDR: это поведение абсолютно нормально.

Почему управление арендой не может быть гладким и без исключений: чтобы дать больше контроля над ситуацией разработчику.

Действительно длинная история - все от основ EventProcessorhost (настоящим EPH - очень похоже на то, что __consumer_offset topic делает для Kafka Consumers - владение разделами и хранилище контрольных точек) написано самой командой Microsoft Azure EventHubs - чтобы перевести все EventHubs partition receiver Gu в простой onReceive(Events) обратный вызов.

EPH используется для решения 2 основных, известных проблем при чтении разделенных потоков с высокой пропускной способностью, таких как EventHubs:

  1. отказоустойчивый приемный конвейер - например: более простая версия проблемы - если хост, на котором запущен PartitionReceiver, умирает и возвращается - ему необходимо возобновить обработку с того места, где он ушел. Чтобы запомнить последний успешно обработанный EventData, EPH использует blob, предоставленный конструктору EPH, для хранения контрольных точек - всякий раз, когда пользователь вызывает context.CheckpointAsync(). В конце концов, когда хост-процесс умирает (например, внезапно перезагружается или сталкивается с аппаратной ошибкой и никогда/не возвращается) - любой экземпляр EPH может забрать эту задачу и возобновить ее с Checkpoint.

  2. Сбалансировать/распределить разделы между экземплярами EPH - скажем, если имеется 10 разделов и 2 экземпляра EPH, обрабатывающих события из этих 10 разделов - нам нужен способ разделить разделы между экземплярами (компонент PartitionManager Библиотека EPH делает это). Мы используем Azure Storage - Blob LeaseManagement-feature для реализации этого. Начиная с версии 2.2.10 - чтобы упростить проблему, EPH предполагает, что все разделы загружены одинаково.

Теперь попробуем посмотреть, что происходит: Итак, для начала, в приведенном выше примере разделов-концентраторов событий 10 и экземпляров 2 EPH из них обрабатывают события:

  1. скажем, первый экземпляр EPH - EPH1 запущен, сначала один, и часть запуска, он создал приемники для всех 10 разделов и обрабатывает события. При запуске - EPH1 объявит, что владеет всеми этими разделами 10, приобретая аренду на BLOB-объектах хранения 10, представляющих эти разделы-концентраторы событий 10 (со стандартным nomenclature - который внутренне создает EPH в учетной записи хранения - из StorageConnectionString, переданного в ctor). Аренда будет приобретаться в течение установленного времени, после чего экземпляр EPH потеряет право собственности на этот раздел.
  2. EPH1 постоянно announces время от времени - что он все еще владеет этими разделами - с помощью renewing сдает в аренду на блоб. Частота renewal, наряду с другими полезными настройками, может быть выполнена с помощью PartitionManagerOptions
  3. теперь, допустим, запускается EPH2 - и вы также подали тот же AzureStorageAccount, что и EPH1, в ctor из EPH2. Прямо сейчас у него есть 0 разделы для обработки. Таким образом, чтобы достичь баланса разделов между экземплярами EPH, он будет продолжать и download список всех leaseblobs, который имеет отображение owner на partitionId. Исходя из этого, он будет STEAL арендовать свою справедливую долю partitions - в нашем примере 5 и объявит эту информацию об этом lease blob. Как часть этого, EPH2 считывает последнюю контрольную точку, написанную PartitionX, для которой он хочет украсть аренду, и продвигается вперед и создает соответствующий PartitionReceiver с EPOCH, такой же, как тот, что в Checkpoint.
  4. В результате EPH1 потеряет право собственности на эти 5 partitions и будет сталкиваться с различными ошибками в зависимости от точного состояния, в котором он находится.
    • если EPH1 фактически вызывает вызов PartitionReceiver.Receive() - когда EPH2 создает PartitionReceiver на том же приемнике, - EPH1 будет испытывать ReceiverDisconnectedException. Это в конечном итоге вызовет IEventProcessor.Close(CloseReason=LeaseLost). Обратите внимание, что вероятность попадания в это конкретное исключение выше, если принимаемые сообщения больше или PrefetchCount меньше - так как в обоих случаях получатель будет выполнять более агрессивный ввод/вывод.
    • если EPH1 находится в состоянии checkpointing lease или renewing lease, а EPH2 stole в аренду, EventProcessorOptions.ExceptionReceived eventHandler будет сигнализироваться с помощью leaselostException ( с 409 конфликтной ошибкой в leaseblob), которая также в конечном итоге вызывает IEventProcess.Close(LeaseLost).

Почему управление арендой не может быть гладким и Исключение свободных:

Чтобы сохранить потребителя простым и безошибочным, исключения, связанные с управлением арендой, могли быть проглочены EPH и вообще не уведомлены для кода пользователя. Тем не менее, мы поняли, что использование LeaseLostException может дать клиентам возможность находить интересные ошибки в обратном вызове IEventProcessor.ProcessEvents(), для которого это может быть симптомом - частые перемещения разделов

  • незначительное отключение сети на конкретном компьютере - из-за которого EPH1 не может renew сдать в аренду и возвращается! - и представьте, если н/ж этой машины простаивает в течение дня - экземпляры EPH будут играть ping-pong с Partitions! Эта машина будет постоянно пытаться украсть аренду у другой машины, что является законным с точки зрения EPH, но для пользователя EPH это полная катастрофа, поскольку она полностью мешает каналу обработки. EPH - в точности увидит ReceiverDisconnectedException, когда н/ж вернется на этот облезлый т/с! Мы думаем, что самый лучший и эффективный способ - дать возможность разработчику почувствовать это!
  • или простой сценарий, например, с ошибкой в логике ProcessEvents - которая генерирует необработанные исключения, которые являются фатальными и приводит к остановке всего процесса - например: событие отравления. Этот раздел будет много перемещаться.
  • клиенты, выполняющие операции записи/удаления в той же учетной записи хранения, которую также использует EPH - по ошибке (например, сценарий автоматической очистки) и т.д.
  • последнее, но не менее важное, чего мы никогда не хотели бы, скажем, 5 минут outage в Azure d.c, где расположен конкретный EventHub.Partition, скажем, инцидент н/ж. Разделы будут перемещаться между экземплярами EPH.

По сути, в большинстве ситуаций было бы сложно - для нас обнаружить разницу. между этими ситуациями и законным leaseLost из-за балансировки, и мы хотим делегировать контроль над этими ситуациями разработчику.

больше на Event Hubs...