Как стать автором
Обновить

Асинхронный флаг без мистики

Время на прочтение7 мин
Количество просмотров262
Автор оригинала: Flowable

Многие элементы процессов и кейсов в Flowable имеют свойство под названием «Асинхронность». Хотя это свойство сильно влияет на производительность, надежность и даже на пользовательский опыт, его часто игнорируют или недооценивают. Эта статья предназначена для того, чтобы помочь аналитикам и разработчикам понять его значимость.

Что означает «асинхронность»?

Многие коллеги, специализирующиеся на моделировании процессов, думают так же, как и я, когда впервые увидел этот флаг:

«Асинхронность означает, что это будет выполнено позже, в фоне, и процесс продолжит выполнение других шагов».

На самом деле это неправда (или верно лишь частично):

«Асинхронность означает, что это будет выполнено позже, в фоне (верно), и процесс продолжит выполнение других шагов (неверно)».

Семантика процесса не изменяется из-за этого флага. Порядок выполнения остаётся прежним.

Посмотрите на модель процесса ниже: Async Task 1 всегда будет выполнен до Async Task 2, независимо от значения флага асинхронности. Если процессу требуется параллельность, это должно быть достигнуто с помощью моделирования, например, через Parallel Gateway.

Если вы хотите увидеть это в действии, импортируйте пример приложения. Посмотрите, как устроен процесс "Process Two Async Tasks". Оба скрипт-таска просто записывают строку в лог.

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

Hello from Task1
Hello from Task2

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

Как движок выполняет асинхронный шаг?

Движок Flowable устроен так, чтобы последовательно выполнять шаги в процессе или кейсе до тех пор, пока не будет достигнуто состояние ожидания (wait-state) или пока процесс не завершится. Таким состоянием ожидания может быть пользовательская задача, промежуточное сообщение или таймер.

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

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

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

  1. Новая работа (Job) для возобновления выполнения процесса, начиная с сервисной задачи.

  2. Состояние процесса (записанные переменные, уже выполненные шаги и так далее).

После того как транзакция будет зафиксирована (committed), эти изменения станут видимыми для других компонентов движка процессов. В результате первого изменения — создания новой задачи (Job) — Асинхронный Исполнитель Задач (Asynchronous Job Executor) сможет её обнаружить. Следовательно, задача, которая возобновит выполнение процесса начиная с сервисной задачи, будет выполнена асинхронно в одном из потоков, принадлежащих Асинхронному Исполнителю. Обратите внимание: задача (Job) выполняет не только саму сервисную задачу, но также и последующие шаги процесса.

К слову, по умолчанию в движке Flowable есть механизм повторных попыток выполнения (retry mechanism) асинхронных задач. Это значит, что если выполнение не удаётся с первого раза, оно будет повторено позже, всего до трёх попыток (это значение настраивается через параметр
asyncExecutorNumberOfRetries). Эта функция особенно полезна при обращении к удалённым сервисам, которые могут быть временно недоступны из-за сбоев или проблем с сетью. В подобных случаях механизм повторов помогает справиться с такими сбоями.

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

Пример процесса заказа пиццы

Форма первой пользовательской задачи может выглядеть следующим образом:

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

После того как скриптовая задача успешно зафиксирует заказ, пользователю будет показана новая задача для подтверждения заказа:

Модель с использованием синхронных шагов

В Flowable Task, как только приложение опубликовано, процесс запускается сразу после нажатия кнопки:

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

Следующая диаграмма отображает транзакции:

До этого момента, с точки зрения «счастливого сценария», всё сработало: клиент создал заказ, система его зарегистрировала, и пользователю отобразилось подтверждение.

Результат: довольный клиент.

Реальный мир: синхронные шаги и ненадёжная система

А что, если система, ответственная за приём заказов, не так надёжна? Или в тот момент была недоступна внутренняя сеть? Мы можем смоделировать такие условия этим кодом для скриптовой задачи:

if (Math.random() > 0.8) {
    println "Failure sending order!"
    throw new RuntimeException("Failure!")
} else {
    println "Order sent"
}

Новая таблица исполнения выглядит следующим образом, отличия от «счастливого сценария» выделены синим цветом:

И соответствующая диаграмма транзакций:

В этот момент, в зависимости от уровня раздражения, пользователь может нажать кнопку «Завершить» повторно. Даже если со второго раза всё сработает, компания-пиццерия уже потеряла: в лучшем случае пользователь заметил, что страница ненадёжна, и получил плохое впечатление. В худшем — закрыл браузер и больше не вернулся.

Результат: потерянный клиент / непрофессиональный имидж.

Реальный мир: синхронные шаги и медленная система

Теперь предположим, что система приёма заказов надёжна, но работает медленно (сталкивались с таким при заказе еды онлайн?). Допустим, подтверждение заказа занимает около 30 секунд. Результат показан ниже:

Транзакции полностью совпадают с «счастливым сценарием», за исключением того, что T2 выполняется заметно дольше:

Клиент начал нервничать, когда браузер слишком долго показывал «крутилку» — он решил, что ваша система медленная.

Результат: плохой пользовательский опыт. Система (а значит и ваша компания) выглядит непрофессионально!

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

Модель с использованием асинхронных шагов

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

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

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

Как и ожидалось, в «счастливом сценарии» всё прошло гладко: заказ быстро сохраняется, и задача с подтверждением создаётся сразу после обработки заказа. Теперь мы можем подумать об улучшении процесса; на самом деле, вместо задачи пользователя, подтверждение можно реализовать через отправку email.Такой подход сделает процесс более удобным для клиента — не придётся обновлять страницу в ожидании задачи подтверждения.

Результат: довольный клиент.

Реальный мир с асинхронными шагами: ненадёжная система

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

Модель выглядит так:

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

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

Реальный мир с асинхронными шагами: медленная система

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

Результат: довольный клиент.

Ошибок не было видно, пицца доставлена. Низкая производительность системы размещения заказов действительно влияет на общую длительность, но если среднее время доставки около 15 минут, то отклонение в 30 секунд не повлияет на удовлетворённость клиента.

Итоги

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

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

Подписывайтесь на наши телеграм каналы:

Jmix.ru — платформа быстрой разработки B2B и B2G веб-приложений на Java.
BPM Developers — про бизнес-процессы: новости, гайды, полезная информация и юмор.

Теги:
Хабы:
-1
Комментарии0

Публикации

Истории

Работа

Ближайшие события

19 марта – 28 апреля
Экспедиция «Рэйдикс»
Нижний НовгородЕкатеринбургНовосибирскВладивостокИжевскКазаньТюменьУфаИркутскЧелябинскСамараХабаровскКрасноярскОмск
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань
20 – 22 июня
Летняя айти-тусовка Summer Merge
Ульяновская область