Распределенный Рабочий Процесс В Php. Часть 1

Мы разрабатываем огромное количество сложного программного обеспечения для автоматизации и предприятия, и Workflow для нас — большая и болезненная проблема.

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

А также как сделать внутри процессов таймеры на 30 дней.

И самое главное, как все это вырезать на PHP. Меня зовут Антон Титов .

Я занимаюсь коммерческой разработкой более 17 лет. Я являюсь соавтором Spiral Framework, RoadRunner и Cycle ORM. Основной стек: PHP и Golang. Разговор пойдет о нашей разработке Temporal PHP SDK, который помогает решить все вышеперечисленные сложные проблемы.



Распределенный рабочий процесс в PHP. Часть 1

Статья получилась большая.

Поэтому я разделил его на теоретическую и практическую части.

Сначала, конечно, теория.

И чтобы ввести вас в курс дела, начну с истории.

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

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



Первая интеграция

Первая интеграция, которую вы делаете — с сервисом Яндекс.

Еда:

Распределенный рабочий процесс в PHP. Часть 1

Получается, клиент приготовил пиццу, нажал кнопку, кто-то заказал ее в Яндекс.

Еде и получил трек доставки, а пицца отправлена пользователю — все счастливы и довольны.

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

Распределенный рабочий процесс в PHP. Часть 1

Вы интегрируетесь с терминалом на кухне и с менеджерами.

Они также будут нажимать кнопки и обновлять статусы.

Это не очень сложная задача.



Вторая интеграция

Чуть позже к вам прибегает директор пиццерии и говорит, что Яндекс.

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

Просто добавляете пару if, делаете отдельную очередь и точку http:

Распределенный рабочий процесс в PHP. Часть 1



Третья интеграция

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

Однако это тоже пара if-фишек и поначалу все работает. Однако позже выясняется, что сервис разрабатывался на коленках на Perl, поэтому любит зависать и отваливаться.

Но добавляешь пару crontabов для проверки таймаутов, и повторяешь попытку — и система вроде продолжает работать:

Распределенный рабочий процесс в PHP. Часть 1

Однако возникает новая проблема: повара забывают принимать заказы — в результате клиенты недовольны, а менеджеры ругаются.

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

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

В какой-то момент менеджер предлагает вам возможность отменить заказ.

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

Например, во время принятия заказа:

Распределенный рабочий процесс в PHP. Часть 1



Похоже, мы закончили!

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

На диаграмме видно, что она сильно перемешана не только с бизнес-задачой, но и со структурной логикой — обратными вызовами, таймаутами, повторами и так далее.

Можно написать такую систему в лоб — взять немного редиски, немного RabbitMQ, добавить пару кронных задач и как-то склеить, но в итоге у вас получится монстр с кучей проблем:

Распределенный рабочий процесс в PHP. Часть 1

Масштабировать его будет очень сложно.

Поэтому, если ту компанию купит крупный холдинг и скажет: «Давайте доставлять не 100 пицц в день, а 10 тысяч в час», то вам придется все переписывать с нуля.

Это все Workflow, а система, которая передает данные между этапами, называется оркестровкой.



Давайте поговорим о рабочем процессе и оркестрации.

Чтобы объяснить, что здесь можно сделать, давайте сначала немного взглянем на то, что такое Workflow – более простыми словами.

Очень часто его представляют в виде схемы со стрелками и набором шагов.

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

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

Распределенный рабочий процесс в PHP. Часть 1

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

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

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

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

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

Поэтому никаких ограничений здесь быть не должно.

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

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

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

Поэтому нам нужна система, которая позволит влиять на состояние бизнес-процесса.

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



Наблюдаемое состояние

Это возможность понять, где мы сейчас находимся в нашем бизнес-процессе.

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

Поэтому хотелось бы иметь возможность поиска по Workflow: по состоянию, то есть по стадии выполнения.

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

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



Устойчивое состояние

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

В противном случае в Workflow нет смысла.

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

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

Очень неприятно, когда что-то падает и мы не знаем, что именно.

Логов нет, системы нет, а у нас служба доставки дала сбой или повар какой-то странный? В идеале вы хотите понять всю историю ошибки.

Например, доставка не пришла, потому что пицца была неправильной.

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

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

Как происходит распространение ошибок или распределенная трассировка стека ошибок.

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

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

Также необходимо иметь систему ретрея — в идеале с бэкоффом, чтобы в случае отвала API можно было потихоньку попытаться до него достучаться.



Рабочий процесс – где он используется?

  • распределенных вычислений;
  • финансовые операции;
  • логистика, инвентарь;
  • синхронизация внешних систем;
  • накопительная рассылка уведомлений;
  • пользовательские кассы и корзины покупок;
  • документооборот;
  • управление инфраструктурой;
  • обработка файлов;
  • мониторинг, CI/CD;
  • просто чтобы не упал.

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

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



Варианты реализации

Мы рассмотрели пример, у нас даже есть какая-то теоретическая база.

Давайте подумаем, как это можно реализовать.



Напишем об очередях и базах данных!

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

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

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

Распределенный рабочий процесс в PHP. Часть 1

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



Может, на Кафке?

Если вы более предприимчивы, вы можете попробовать Kafka:

Распределенный рабочий процесс в PHP. Часть 1

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

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

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

Рабочий процесс - ну тогда декларативно!

Мы, наверное, все ленивые разработчики, поэтому давайте просто найдем готовый инструмент. Скорее всего, это будет декларативная реализация Workflow:

Распределенный рабочий процесс в PHP. Часть 1

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

BPMN в лучшем случае.

Например, Apache Airflow или Camunda, где мы можем рисовать диаграммы, передавать данные между ними и таким образом координировать нашу систему.

Вариаций декларативных Workflow очень много, есть даже отдельный репозиторий assemble_workflow. Но все они имеют множество ограничений.



Рабочий процесс — ограничения DAG

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

То есть вы описываете желаемое поведение Workflow в виде графика.

Соответственно нет динамики.

Для небольших задач этого добиться очень сложно.

Ну и зачем затягивать визуальный BPMN, чтобы заказать пиццу? Легче написать это на колене.

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

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

Например, не существует общего стандарта, поэтому, если вы изучили Netflix Conductor, а затем перешли на Apache Airflow, изучите его еще раз.

У вас появятся новые примитивы, новые отображения, и двигаться будет очень сложно.

И всё очень печально: придётся писать самому — это очень долго, дорого, сложно — а крайних случаев миллион.

У готовых движков много ограничений, причем половина из них платные.

И почти везде требуется DSL. У нас много корпоративного программного обеспечения, и Workflow для нас — старая боль.

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

  • описание Workflow по коду;
  • вы можете создать свой собственный DSL;
  • изоляция данных;
  • бесконечный масштаб;
  • Ядро Голанга.

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

Но, кроме этого, это также гарантирует, что система не выйдет из строя.

Поскольку движок императивный, то есть написанный в вашем коде, вы можете взять свой готовый DSL, переписать его в Temporal и поставить прямо поверх него.

Многие пользователи этого движка так делают, это реальный вариант использования.

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

Он используется под их капотом в качестве основного механизма координации всех финансовых транзакций.

Большим бонусом для нас стала возможность использовать ядро Golang и Golang SDK. Поскольку у нас много продуктов, написанных на Golang, в частности, РоудРаннер .

Но все оказалось еще интереснее.

Авторы Uber Cadence открыли отдельный стартап, получили инвестиции и дали ему название Временной , и все это было лицензировано по лицензии MIT. К тому же это были бывшие россияне, поэтому нам было проще общаться с ними по вопросам интеграции и разработки API. Для нас это был полный беспроигрышный вариант — масштабируемая система MIT, которую можно собрать дома, описывает Workflow в коде без всякого DSL, а также можно общаться на русском языке.

А на что способен Temporal PHP SDK, мы посмотрим в следующей, более практической части.

Конференция PHP Россия 2022 это отличное место, чтобы поделиться своим опытом с сообществом.

Он пройдет 12 и 13 сентября в Москве, в отеле Radisson SAS Славянская.

Фокус: развитие экосистемы (сам PHP, стандарты, фреймворки, библиотеки, OpenSource); опыт крупных компаний по построению сложных проектов на PHP и лучшие практики.

И, конечно же, темы ежедневного развития.

Заявки на выступления принимаются до 25 мая.

Расходы на проезд и проживание спикеров покрываются.

Все подробности на Веб-сайт .

Теги: #Алгоритмы #программирование #отказоустойчивость #оркестрация #Go #php #temporal #workflow #temporal php sdk #Uber Cadence #enterprise
Вместе с данным постом часто просматривают: