Шаблон супервизора агента планировщика

Azure

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

Контекст и проблема

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

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

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

Решение

Шаблон "планировщик, агент, контролер" определяет следующие субъекты. Эти субъекты координируют действия, выполняемые в составе общей задачи.

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

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

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

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

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

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

Рис. 1. Субъекты в шаблоне

Примечание.

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

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

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

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

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

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

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

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

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

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

Проблемы и рекомендации

При выборе схемы реализации этого шаблона следует учитывать следующие моменты.

  • Этот шаблон может быть сложным в реализации и требует тщательного тестирования всех возможных режимов сбоя системы.

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

  • Важно выбрать правильную частоту для запуска контролера. Он должен выполняться достаточно часто, чтобы сбой любого шага не вызывал длительную блокировку всего приложения, но при этом не слишком часто — во избежание дополнительных затрат.

  • Шаги, выполняемые агентом, можно запускать более одного раза. Логика, которая реализует эти действия, должна быть идемпотентной.

Когда следует использовать этот шаблон

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

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

Проектирование рабочей нагрузки

Архитектор должен оценить, как шаблон диспетчера агентов планировщика может использоваться в проектировании рабочей нагрузки для решения целей и принципов, описанных в основных принципах Платформы Azure Well-Architected Framework. Например:

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

- ИЗБЫТОЧНОСТЬ RE:05
- RE:07 Самовосстановление
Эффективность производительности помогает рабочей нагрузке эффективно соответствовать требованиям путем оптимизации масштабирования, данных, кода. В этом шаблоне используются метрики производительности и емкости для обнаружения текущих задач использования и маршрутизации в агент с емкостью. Вы также можете использовать его для определения приоритета выполнения более высокой приоритетной работы над более низкой приоритетной работой.

- Pe:05 Масштабирование и секционирование
- Критически важные потоки PE:09

Как и любое решение по проектированию, рассмотрите любые компромиссы по целям других столпов, которые могут быть представлены с этим шаблоном.

Пример

В Microsoft Azure развернуто веб-приложение, в котором реализована система электронной коммерции. Пользователи могут запускать это приложение для просмотра доступных товаров и размещения заказов. Пользовательский интерфейс работает как веб-роль, а элементы обработки заказов реализованы в приложении как набор рабочих ролей. Логика обработки заказов включает элементы с доступом к удаленной службе, и этот аспект системы уязвим для временных или длительных сбоев. По этой причине разработчики применяют шаблон "планировщик, агент, контролер" для реализации обработки заказов в системе.

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

Запись о состоянии, которую процесс отправки создает для заказа, содержит следующие сведения:

  • OrderID. Идентификатор заказа в базе данных заказов.

  • LockedBy. Идентификатор экземпляра рабочей роли, которая обрабатывает этот заказ. Планировщик может запустить несколько экземпляров рабочей роли, но каждый заказ должен обрабатываться только одним экземпляром.

  • CompleteBy. Время, к которому должен быть обработан заказ.

  • ProcessState. Текущее состояние задачи, обрабатывающей заказ. Возможные состояния:

    • Pending. Заказ был создан, но обработка еще не началась.
    • Обработка. Заказ обрабатывается в настоящее время.
    • Processed. Заказ успешно обработан.
    • Ошибка. Сбой при обработке заказа.
  • FailureCount. Количество попыток обработки этого заказа.

Поле OrderID в этом наборе сведений о состоянии содержит значение идентификатора нового заказа. Поля LockedBy и CompleteBy имеют значение null, поле ProcessState имеет значение Pending, а поле FailureCount — значение 0.

Примечание.

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

Планировщик также выполняется в рабочей роли и содержит бизнес-логику для обработки заказа. Экземпляр планировщика, выполняющего опрос относительно наличия новых заказов, проверяет записи в хранилище состояний, в которых поле LockedBy имеет значение NULL, а поле ProcessState — значение Pending. Когда планировщик находит новый заказ, он немедленно заносит в поле LockedBy значение своего идентификатора экземпляра, в поле CompleteBy фиксирует текущее время, а в поле ProcessState указывает состояние Processing. Код выполняет атомарную операцию в монопольном режиме, что не позволяет двум параллельно запущенным экземплярам планировщика обрабатывать один заказ одновременно.

После этого планировщик запускает рабочий процесс асинхронной обработки заказа, передавая ему значение поля OrderID из хранилища состояний. Рабочий процесс, обрабатывающий заказ, извлекает сведения о заказе из базы данных заказов и выполняет свою работу. Когда шаг рабочего процесса обработки заказа должен обратиться к удаленной службе, он использует агент. Шаг рабочего процесса взаимодействует с агентом с помощью пары очередей сообщений служебной шины Azure, которые выполняют функции канала "запрос — ответ". На рисунке показано высокоуровневое представление решения.

Рис. 2. Использование шаблона диспетчера агентов планировщика для обработки заказов в решении Azure

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

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

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

Контролер периодически проверяет хранилище состояний на наличие заказов с истекшим сроком выполнения. Обнаружив такую запись, контролер увеличивает для нее значение поля FailureCount. Пока значение счетчика сбоев остается ниже определенного порогового значения, контролер задает для поля LockedBy значение NULL, вносит в поле CompleteBy новое время окончания срока выполнения и меняет значение поля ProcessState на Pending. Экземпляр планировщика подберет этот заказ и обработает его, как описано выше. Если значение счетчика сбоев превышает заданное пороговое значение, сбой можно считать постоянным. Тогда планировщик устанавливает значение Error для состояния заказа и создает событие с предупреждением для оператора.

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

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

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

Следующие шаги

При реализации этого шаблона следует принять во внимание следующие рекомендации:

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

  • Reference 6: A Saga on Sagas (Справочник 6. Сага о сагах). Пример, демонстрирующий использование диспетчера процессов в шаблоне CQRS (часть руководства по CQRS).

  • Планировщик Microsoft Azure

При реализации этого шаблона могут быть также важны следующие шаблоны:

  • Шаблон повторов. Агент может использовать этот шаблон для прозрачного повторения операций доступа к удаленной службе или ресурсу, если ранее они завершались сбоем. Используйте его, если есть основания полагать, что сбой является временным и может быть исправлен.

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

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

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

  • Облачная архитектура: шаблон планировщика-агента-руководителя в блоге Clemens Vasters

  • Шаблон диспетчера процессов