Ответ 1
TLDR: это поведение абсолютно нормально.
Почему управление арендой не может быть гладким и без исключений: чтобы дать больше контроля над ситуацией разработчику.
Действительно длинная история - все от основ
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
, приобретая аренду на BLOB-объектах хранения10
, представляющих эти разделы-концентраторы событий10
(со стандартнымnomenclature
- который внутренне создаетEPH
в учетной записи хранения - изStorageConnectionString
, переданного вctor
). Аренда будет приобретаться в течение установленного времени, после чего экземплярEPH
потеряет право собственности на этот раздел. EPH1
постоянноannounces
время от времени - что он все еще владеет этими разделами - с помощьюrenewing
сдает в аренду на блоб. Частотаrenewal
, наряду с другими полезными настройками, может быть выполнена с помощьюPartitionManagerOptions
- теперь, допустим, запускается
EPH2
- и вы также подали тот жеAzureStorageAccount
, что иEPH1
, вctor
изEPH2
. Прямо сейчас у него есть0
разделы для обработки. Таким образом, чтобы достичь баланса разделов между экземплярамиEPH
, он будет продолжать иdownload
список всехleaseblobs
, который имеет отображениеowner
наpartitionId
. Исходя из этого, он будетSTEAL
арендовать свою справедливую долюpartitions
- в нашем примере5
и объявит эту информацию об этомlease blob
. Как часть этого,EPH2
считывает последнюю контрольную точку, написаннуюPartitionX
, для которой он хочет украсть аренду, и продвигается вперед и создает соответствующийPartitionReceiver
сEPOCH
, такой же, как тот, что вCheckpoint
. - В результате
EPH1
потеряет право собственности на эти 5partitions
и будет сталкиваться с различными ошибками в зависимости от точного состояния, в котором он находится.- если
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 из-за балансировки, и мы хотим делегировать контроль над этими ситуациями разработчику.