Надежная обработка событий в Функциях Azure

Обработка событий является одним из наиболее распространенных сценариев, связанных с бессерверной архитектурой. В этой статье объясняется, как создать надежный обработчик с помощью Функций Azure, чтобы избежать потери сообщений.

Проблемы потоков событий в распределенных системах

Рассмотрим систему, которая отправляет события с постоянной частотой 100 событий в секунду. При такой скорости в течение нескольких минут параллельные экземпляры Функций могут потреблять по 100 входящих событий каждую секунду.

Однако возможны перечисленные ниже осложнения.

  • Что делать, если издатель события отправляет поврежденное событие?
  • Что делать, если экземпляр Функций сталкивается с необработанными исключениями?
  • Что произойдет, если нижестоящая система отключается?

Как обрабатывать такие ситуации, сохраняя пропускную способность приложения?

Очереди естественным образом реализуют надежный обмен сообщениями. В сочетании с триггером Функций Azure функция создает блокировку для сообщения очереди. Если обработка завершается неудачей, блокировка освобождается, чтобы позволить другому экземпляру повторить попытку обработки. Затем обработка продолжится до тех пор, пока сообщение не будет успешно обслужено или добавлено в очередь подозрительных сообщений.

Даже если одно сообщение в очереди остается в цикле повтора, другие параллельные выполнения продолжают выводить из очереди оставшиеся события. В результате общая пропускная способность не страдает от одного проблемного сообщения. При этом очереди хранилища не гарантируют определенного порядка и не оптимизированы с учетом высоких требований к пропускной способности, предъявляемых Центрами событий.

Кроме того, в Центрах событий Azure не реализована концепция блокировки. Для реализации таких характеристик и функций, как высокая пропускная способность, разные группы потребителей и воспроизводимость, обработка событий в Центрах событий больше напоминает работу видеопроигрывателя. События считываются из одной точки в потоке на каждую секцию. С помощью указателя можно считывать сообщения из этой точки вперед или назад, однако для обработки событий указатель необходимо перемещать.

Если при возникновении ошибки в потоке вы решаете оставить указатель на прежнем месте, обработка событий блокируется до тех пор, пока он не будет перемещен. Иными словами, если перемещение указателя останавливается для решения проблем с обработкой одного события, необработанные события начинают накапливаться.

Функции Azure избегают взаимоблокировок путем перемещения указателя потока независимо от успешности или сбоя обработки. Поскольку указатель постоянно перемещается, ваши функции должны правильно обрабатывать возникающие ошибки.

Как Функции Azure потребляют события Центров событий

Функции Azure потребляют события Центров событий путем циклического выполнения следующих действий:

  1. Для каждой секции концентратора событий создается и сохраняется указатель в службе хранилища Azure.
  2. При получении новых сообщений (по умолчанию в пакете) узел пытается запустить функцию с этим пакетом.
  3. Если функция завершает выполнение (с исключением или без него), указатель перемещается и в учетной записи хранения сохраняется контрольная точка.
  4. Если условия не позволяют завершить выполнение функции, узел не перемещает указатель. Если указатель не перемещается, дальнейшие проверки обрабатывают те же сообщения.
  5. Повторяются шаги 2–4.

В такой схеме поведения есть несколько важных моментов.

  • Необработанные исключения могут привести к потере сообщений. Выполнения, при которых возникает исключение, все равно продвигают указатель. Настройка политики повтора задерживает перемещение указателя до тех пор, пока не будет выполнена вся политика повтора.
  • Функции гарантируют доставку по крайней мере один раз. В вашем коде и зависимых системах может потребоваться учесть тот факт, что одно и то же сообщение может быть получено дважды.

Обработка исключений

Как правило, каждая функция должна содержать на самом верхнем уровне кода блок try/catch. В частности, содержать блок catch должны все функции, использующие события Центров событий. Таким образом при возникновении исключения блок catch обрабатывает ошибку перед перемещением указателя.

Механизмы и политики повтора

Некоторые исключения являются временными и не повторяются при попытке выполнить операцию позже. Именно поэтому на первом шаге всегда нужно пытаться повторить операцию. Можно использовать политики повтора приложения-функции или создать логику повтора в рамках выполнения функции.

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

  • Выполняется попытка вставить сообщение три раза (возможно, с задержкой между повторами).
  • Если результатом всех попыток является сбой, сообщение добавляется в очередь, чтобы продолжить обработку событий в потоке.
  • Поврежденные или необработанные сообщения обрабатываются позже.

Примечание

Polly — пример библиотеки, где реализованы механизмы обеспечения устойчивости и обработки временных ошибок для приложений C#.

Ошибки, не связанные с исключениями

Иногда проблемы возникают даже при отсутствии ошибок. Например, рассмотрим сбой, который происходит в процессе выполнения. Если выполнение в таком сценарии функции не завершается, указатель смещения не перемещается. Если указатель не перемещается, любой экземпляр, который запускается после неудачного выполнения, продолжает считывать те же сообщения. Такой сценарий гарантирует по крайней мере однократную обработку.

Гарантия того, что каждое сообщение обрабатывается хотя бы один раз, означает, что некоторые сообщения могут обрабатываться и по несколько раз. Приложения-функции должны учитывать такую возможность, и в них должны быть реализованы принципы идемпотентности.

Остановка и перезапуск выполнения

Иногда ошибки бывают приемлемыми, но что делать, если в приложении возникают серьезные сбои? Можно попробовать отключить срабатывание по событиям до тех пор, пока система не вернется в работоспособное состояние. Возможность приостановить обработку часто реализуется с помощью шаблона автоматического выключения. Шаблон автоматического выключения позволяет приложению "разрывать цепь" обработки событий и возобновлять ее позже.

Для реализации автоматического прерывания обработки событий необходимы два элемента:

  • общее состояние по всем экземплярам для отслеживания и мониторинга работоспособности цепи;
  • главный процесс, который управляет состоянием цепи (разомкнута или замкнута).

Особенности реализации могут быть разными, но для отслеживания общего состояния между экземплярами необходим механизм хранения. Состояние можно хранить в службе хранилища Azure, кэше Redis или любой другой учетной записи, доступной для коллекции функций.

Естественным выбором для управления состоянием рабочего процесса и цепи являются Azure Logic Apps или устойчивые функции. Другие службы могут работать не хуже, но в этом примере мы применяем приложения логики. С помощью приложений логики можно приостанавливать и перезапускать выполнение функции, позволяя тем самым реализовать шаблон автоматического выключения.

Определение порогового значения сбоя для экземпляров

Чтобы обеспечить одновременную обработку событий несколькими экземплярами, необходимо хранить общее внешнее состояние, позволяющее отслеживать работоспособность цепи.

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

  • Если в течение 30 секунд во всех экземплярах возникает более 100 неудачных попыток, нужно разорвать цепь и остановить срабатывание по новым сообщениям.

Особенности реализации зависят от конкретных потребностей, но в целом можно создать систему, которая:

  1. Ведет журнал ошибок в учетной записи хранения (службе хранилища Azure, Redis и т. д.).
  2. При занесении в журнал сведений о новом сбое проверяет текущее число ошибок, чтобы проконтролировать соответствие порогу (например, более 100 сбоев за последние 30 секунд).
  3. Если пороговое значение достигается, в службу "Сетка событий Azure" системе направляется инструкция разорвать цепь.

Управление состоянием цепи с помощью Azure Logic Apps

Ниже описан один из способов создать приложение логики Azure, позволяющее обработку в приложении-функции.

В Azure Logic Apps есть встроенные соединители для различных служб и поддерживается оркестрация состояния функций, и эта служба естественным образом подходит для управления состоянием цепи. Обнаружив необходимость разорвать цепь, можно создать приложение логики для реализации следующего рабочего процесса:

  1. Активировать рабочий процесс Сетки событий и остановить работу функции Azure (с помощью соединителя ресурсов Azure).
  2. Отправить электронное письмо с уведомлением, предоставив возможность перезапуска рабочего процесса.

Получатель письма может проанализировать работоспособность цепи и при необходимости перезапустить ее по ссылке в уведомлении. Когда рабочий процесс перезапускает функцию, сообщения обрабатываются начиная с последней контрольной точки Центра событий.

При таком подходе сообщения не теряются, все сообщения обрабатываются по порядку, и при необходимости можно разорвать цепь на сколько угодно долго.

Ресурсы

Дальнейшие действия

Дополнительные сведения см. в следующих ресурсах: