Жизненный цикл Reliable Services

Reliable Services являются одной из моделей программирования, доступных в Azure Service Fabric. При изучении жизненного цикла Reliable Services наиболее важно разобраться с его основными событиями. Точный порядок событий зависит от сведений о конфигурации.

Как правило, жизненный цикл Reliable Services включает следующие события:

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

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

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

И наконец, следует учитывать условия ошибок и сбоев.

Запуск службы без отслеживания состояния

Жизненный цикл службы без отслеживания состояния достаточно прост. Ниже приведен порядок событий.

  1. Создается служба.
  2. Вызывается метод StatelessService.createServiceInstanceListeners() и открываются все возвращаемые прослушиватели. Для каждого прослушивателя вызывается метод CommunicationListener.openAsync().
  3. Затем параллельно:
    • Вызывается метод (StatelessService.runAsync()) службы runAsync.
    • При наличии вызывается собственный метод onOpenAsync службы. В частности, вызывается StatelessService.onOpenAsync(). Это редко используемое переопределение, но оно доступно.

Завершение работы службы без отслеживания состояния

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

  1. Закрываются все открытые прослушиватели. Для каждого прослушивателя вызывается метод CommunicationListener.closeAsync().
  2. Токен отмены, переданный в runAsync(), отменяется. В результате проверки свойства isCancelled токена отмены возвращается значение true, а при вызове метода throwIfCancellationRequested токена возвращается CancellationException.
  3. После завершения runAsync() вызывается метод StatelessService.onCloseAsync() службы, если он присутствует. Опять же, обычно это не переопределяется, но позволяет безопасно закрыть все ресурсы, остановить все фоновые задачи, завершить сохранение внешнего состояния или закрыть существующие подключения.
  4. После выполнения метода StatelessService.onCloseAsync() объект службы уничтожается.

Запуск службы с отслеживанием состояния

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

  1. Создается служба.
  2. Вызывается метод StatefulServiceBase.onOpenAsync(). Этот вызов редко переопределяется в службе.
  3. Вызывается метод StatefulServiceBase.createServiceReplicaListeners().
    • Если служба является первичной, открываются все возвращенные прослушиватели. Для каждого прослушивателя вызывается метод CommunicationListener.openAsync().
    • Если служба является вторичной, то открываются только прослушиватели, помеченные как listenOnSecondary = true. Открытые прослушиватели для вторичных служб используются реже.
  4. Затем параллельно:
    • Если сейчас служба является первичной, то вызывается метод StatefulServiceBase.runAsync() службы.
    • Вызывается метод StatefulServiceBase.onChangeRoleAsync(). Этот вызов редко переопределяется в службе.

Примечание

Для новой вторичной реплики StatefulServiceBase.onChangeRoleAsync() вызывается дважды: после шага 2, когда он получает статус "Дополнительный в режиме простоя", и еще раз во время шага 4, когда он получает статус "Дополнительный активный". Дополнительные сведения о жизненном цикле реплики и экземпляра см. в статье Жизненный цикл реплики и экземпляра.

Завершение работы службы с отслеживанием состояния

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

  1. Закрываются все открытые прослушиватели. Для каждого прослушивателя вызывается метод CommunicationListener.closeAsync().
  2. Токен отмены, переданный в runAsync(), отменяется. При вызове метода isCancelled() токена отмены возвращается значение true, а при вызове метода throwIfCancellationRequested() токена возвращается OperationCanceledException. Service Fabric ожидает завершения runAsync().

Примечание

Необходимо подождать завершения метода runAsync, если это первичная реплика.

  1. После завершения runAsync() вызывается метод StatefulServiceBase.onCloseAsync() службы. Этот вызов представляет собой редко используемое переопределение, но оно доступно.
  2. После выполнения метода StatefulServiceBase.onCloseAsync() объект службы уничтожается.

Переключения первичной реплики службы с отслеживанием состояния

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

При понижении уровня первичной реплики

Для Service Fabric требуется первичная реплика с пониженным уровнем, чтобы остановить обработку сообщений и выполнение любой фоновой работы. Это выглядит аналогично завершению работы службы. Отличие в том, что служба не уничтожается и не закрывается, так как остается вторичной. После этого происходят следующие события:

  1. Закрываются все открытые прослушиватели. Для каждого прослушивателя вызывается метод CommunicationListener.closeAsync().
  2. Токен отмены, переданный в runAsync(), отменяется. В результате проверки метода isCancelled() токена отмены возвращается значение true. При вызове метода throwIfCancellationRequested() токена возвращается OperationCanceledException. Service Fabric ожидает завершения runAsync().
  3. Открываются прослушиватели, имеющие свойство listenOnSecondary = tru.
  4. Вызывается служба StatefulServiceBase.onChangeRoleAsync(). Этот вызов редко переопределяется в службе.

При повышении уровня вторичной реплики

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

  1. CommunicationListener.closeAsync() вызывается для всех открытых прослушивателей (имеющих свойство listenOnSecondary = true)
  2. Открываются все прослушиватели связи. Для каждого прослушивателя вызывается метод CommunicationListener.openAsync().
  3. Затем параллельно:
    • Вызывается метод службы StatefulServiceBase.runAsync().
    • Вызывается метод StatefulServiceBase.onChangeRoleAsync(). Этот вызов редко переопределяется в службе.

Примечание

createServiceReplicaListeners вызывается только один раз и не вызывается повторно в процессе повышения или понижения уровня реплики; используются те же экземпляры ServiceReplicaListener, но при этом создаются новые экземпляры CommunicationListener (путем вызова метода ServiceReplicaListener.createCommunicationListener) после закрытия предыдущих экземпляров.

Распространенные проблемы во время завершения работы службы с отслеживанием состояния и понижения уровня первичной реплики

Service Fabric изменяет первичную реплику службы с отслеживанием состояния по нескольким причинам. Наиболее распространенными из них являются повторная балансировка кластера и обновление приложения. Во время этих операций очень важно, чтобы служба учитывала cancellationToken. Это правило также применяется во время штатного завершения работы службы, например, как при ее удалении.

Службы, которые не обрабатывают отмену аккуратно, могут вызывать несколько проблем. Эти операции выполняются медленно, так как Service Fabric ожидает корректную остановку служб. В конечном счете это может привести к сбоям обновлений из-за истечения времени ожидания и откату. Если не использовать токен отмены, это может также нарушить балансировку кластеров. Кластеры становятся несбалансированными, потому что узлы нагружаются. Однако реорганизация служб невозможна, так как их перемещение занимает слишком много времени.

Так как это службы с отслеживанием состояния, то, скорее всего, они используют надежные коллекции. Когда в Service Fabric понижается уровень первичной реплики, то в первую очередь отменяется доступ на запись к базовому состоянию. Это приводит ко второму ряду проблем, которые могут повлиять на жизненный цикл службы. Коллекции возвращают исключения на основании времени и операции с репликой (перемещение или завершение работы). Очень важно обрабатывать эти исключения правильно.

Исключения, порождаемые Service Fabric, делятся на постоянные (FabricException) и временные (FabricTransientException). Постоянные исключения должны быть зарегистрированы в журнале и отброшены. Для временных исключений возможен повтор в соответствии с логикой повтора.

Обработка исключений, которые поступают от ReliableCollections, в сочетании с событиями жизненного цикла службы является важной частью тестирования и проверки Reliable Services. Мы рекомендуем всегда запускать службу с нагрузкой. Перед развертыванием в рабочей среде вам также необходимо выполнить обновления и хаотическое тестирование. Эти простые действия помогут проверить правильность реализации службы и обработки событий жизненного цикла.

Примечания о жизненном цикле службы

  • Метод runAsync() и вызовы createServiceInstanceListeners/createServiceReplicaListeners являются необязательны. В службе может использоваться один из них, оба или ни одного. Например, если служба выполняет всю работу в ответ на вызовы пользователя, реализовывать метод runAsync() не нужно. Необходимы только прослушиватели связи и соответствующий код. Создавать и возвращать прослушиватели связи необязательно. Служба может выполнять только фоновую работу, поэтому достаточно реализовать runAsync().
  • Она может успешно завершить runAsync() и вернуться из него. Это не является состоянием сбоя, а указывает на то, что фоновая работа службы выполнена. Для надежных служб с отслеживанием состояния метод runAsync() будет вызываться снова, если служба понижена с первичной, а затем обратно повышена.
  • Если служба выполняет выход из runAsync(), порождая какое-либо непредвиденное исключение, произошел сбой. Объект службы завершает работу, и появляется ошибка о работоспособности.
  • Несмотря на то что возвращение этих методов не ограничивается по времени, вы не сможете выполнять запись, а значит и выполнять какую-либо действительную задачу. После получения запроса на отмену рекомендуется возвращать методы как можно быстрее. Если служба не отвечает на такие вызовы API в течение приемлемого промежутка времени, Service Fabric может принудительно завершить работу службы. Обычно это происходит только во время обновления приложения или при удалении службы. По умолчанию время ожидания составляет 15 минут.
  • Сбои в пути onCloseAsync() приводят к вызову onAbort(). Это является последней наилучшей возможностью для службы очистить и освободить все запрошенные ресурсы. Обычно такой вызов осуществляется при обнаружении на узле постоянной неисправности или когда платформа Service Fabric не может надежно управлять жизненным циклом экземпляра службы из-за внутренних сбоев.
  • OnChangeRoleAsync() вызывается, когда реплика службы с отслеживанием состояния меняет роль, например с первичной на вторичную, или наоборот. Основным репликам присваивается статус записи (им разрешено создавать и записывать надежные коллекции), а дополнительным — статус чтения (могут только читать из существующих надежных коллекций). Большинство операций службы с отслеживанием состояния выполняется в основной реплике. Вторичные реплики могут выполнять проверку, предусматривающую только чтение, создавать отчеты, выполнять интеллектуальный анализ данных, а также другие задания, доступные только для чтения.

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