Pull to refresh

Comments 24

Спасибо, было интересно! Пожелание к повествованию- больше для технических людей, чем для менеждеров и рекламных проспектов. Например картинка синхронного вызова на мой взгляд лишняя и комментарий к ней "Такой интеграционный паттерн обладает достаточно большим набором недостатков.....". Позволит сократить объем.

Спасибо за отзыв! Это моя первая публикация на Хабре, и мне пока сложно почувствовать какая информация является лишней для аудитории, и какого стиля повествования лучше придерживаться. Обязательно учту Ваши пожелания в будущем.
Не очень уловил, как вы управляете версиями процесса. Ну то есть, есть у вас процесс, представимый какой-то диаграммой, BPMN, или там BPMS. Можете ли вы сравнить две версии такого процесса? В чем разница между ними? В чем, в сущности, состоит миграция?

Модель процесса мы описываем кодом на kotlin, и объект модели конструируется вызовом функции:


processModel<InitialPlayer>(name = "InitialPlayer", version = 1) { ... }

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


processModel<InitialPlayer>(name = "InitialPlayer", version = 2) { ... }

Если попытаться загрузить состояние процесса из базы, фреймворк сравнит ранее сохраненный номер версии с номером версии в текущей модели. Раз они отличаются, будет вызван абстрактный метод в классе бизнес-процесса, который я могу переопределить и реализовать функцию миграции персистентного состояния процесса (например, migrateV1toV2). Потом может появиться функция migrateV2toV3 и т.д.
Так может быть реализована "ленивая" миграция. Если нужно мигрировать всю базу разом, тогда можно выбрать из базы данные всех экземпляров с version = 1, поправить их, и сохранить с version = 2.


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

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

А можно несколько примеров, какие сложности с jBPM возникали?
Мы сейчас обдумываем вариант его использования, и тоже в связке с ERP. Интересно послушать, на какие грабли вы наступили.

Про jBPM у нас баек много накопилось, на целую статью хватит…
Приведу несколько наиболее болезненных примеров (это все про версию 6.2, которую мы наиболее активно использовали):


  1. Визуальный редактор неудобен для разработки исполняемых бизнес-процессов. В любом реальном процессе будет код, который приходится писать в обычном текстовом окне. Нет ни подсветки синтаксиса, ни автоформата, ни code complition. Типизация есть, но все ошибки вылезут только при компиляции, и на это уходит время. Копипаст кусков диаграммы в другую диаграмму работает неправильно (хотя мы это пофиксили немного, чтобы хоть как-то копирование можно было использовать). Также приходится ставить всем Eclipse, который мы не используем в разработке других компонентов.
  2. Очень сложная модель хранения состояния бизнес-процессов в базе. Во-первых, она бинарная, поэтому мигрировать состояние процессов вне приложения (или даже просто посмотреть значения свойств экземпляра процесса) не получится. Во-вторых, помимо состояния самого процесса и его активностей, есть состояние drools-сессии, за которым приходится отдельно следить, и правильно подбирать стратегию использования drools-сессии. У нас все нормально заработало только на раздельных сессиях под каждый инстанс процесса (PerProcessInstanceRuntimeManager).
  3. HumanTaskService реализован как отдельная подсистема с собственной персистенцией состояния пользовательских задач, но при этом модификация состояния процесса и задачи происходит всегда синхронно, в одной транзакции. В результате мы несколько раз ловили дэдлоки: один поток двигал процесс, в следствие чего должен был изменить состояние задачи, а другой поток — закрывал задачу, в следствие чего должен был двинуть процесс.
  4. Входные параметры процесса нужно маппить на свойства процесса, чтобы потом с ними можно было поработать, но при этом все свойства будут потом сохранены в базу. У одного из наших клиентов в какой-то момент растаращило базу данных. Оказалось, что этот клиент начал использовать ЭЦП на документах, а в некоторые процессы на вход приходит сложный объект с параметрами, среди которых появился дополнительный параметр с подписанным XML-документом. И вот весь этот документ (а иногда он может быть десятки мегабайт) стал персиститься в состояние инстанса процесса. Пришлось писать дополнительный код, чтобы зачищать из состояния процесса лишнее.
  5. Для сложных процессов пришлось доработать напильником исполняющий механизм: доделали отправку Message (по умолчанию у них почему-то только сигналы можно было отправлять), поправили работу BoundaryEvent в некоторых кейсах, и т.п.

Самое главное забыл:
У нас большинство бизнес-процессов так или иначе завязано на документы, и связь процесса с документом в персистенции jBPM и пользовательских задач оказывается спрятана внутри бинарика. А нам нужен был индекс для получения инстанса процесса под конкретный документ (или задач под конкретный документ). Пришлось дополнительно хранить связь документа с процессом, но мне никогда не нравилось это решение.

На самом деле я бы мог бы такие истории рассказывать и про IBM BPM. Ну то есть, как читать рекламу — так все замечательно, а как начинаешь реально программировать — такое выплывает, что просто ой. Главное — написал давно про это пост — так находятся люди, которые не верят, вы типа не осилили… А то что мы модель процесса из базы вытаскивали, просто чтобы в ней что-то поискать — потому что IDE не умеет, и я когда-то давно скрипт для этого написал, так народ уже лет 6 как этим пользуется и вспоминает с благодарностью, потому что ничего не изменилось с тех пор…
Также интересно почему выбрали jBPM, а не Camunda или Flowable

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

После jBPM мне сложно выбрать что-то готовое. Все альтернативы, которые я смотрел, мне не понравились по тем или иным причинам. Где-то я видел те-же "болезни" jBPM, где-то обнаруживались архитектурные изъяны, которые потребовали бы от нас болезненных компромиссных решений. В итоге решили написать свой фреймворк, под свои задачи и свое архитектурное видение. С другой стороны, очень хорошо что мы несколько лет работали с jBPM. Это позволило нам глубже осознать проблемы, с которыми мы могли столкнуться при создании собственного решения, и обойти их.

Спасибо. ))
У нас ситуация сильно отличается — мы в любом случае не будем писать что то свое, а будем использовать что то готовое.
Наверно, пока и дальше будем смотреть на jBPM, если из готового ничего существенно лучше нет.

Ну camunda это когда-то форк от jBMPM, который нормально допили во всех аспектах (за последними набурками уже давно не слежу).
Он был изначально девелоперфрендли и запустить его можно хоть в спринге (отвязали от jboss давно, когда я ещё консалтил).
Дописали куцу тулов, красивых едиторов и таскменеджеров, платных и бесплатных. От комьюнити тоже были.
А главное эти ребята консалтят и уже как лет 10 имеют полный цыкл, внедряют (пеимущёственно в немецкие) фирмы всех размеров и опыт отдают назад в код.


Прогресс не сотоит на месте, они накппив опыты решили сделать даже горизонтально распределяемую альтернативу камунде,
https://zeebe.io/.
но мне не довелось попробовать...

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

Это касается всех средств разработки для любой BPM. Строчить мышкой код — дело нудное и непродуктивное. Кроме того, у большинства производителей средства разработки мало что кривые, еще и глючные. После этого хочется кричать "дайте мне обычный текстовый редактор!".


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


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

Сам движок — это еще не BPMS, и их существуют десятки похожих. Преимущество jBPM перед другими — это то, что он умеет версионировать не только сам процесс, но и весь артефакт, включая классы.


Недостатки синхронных вызовов как интеграционного паттерна

Описанные недостатки типа Quality-of-Service не относятся как таковые к способу вызова. Архитектурное отличие синхронных вызовов в том, что клиент обязан обрабатывать нештатные ситуации, и включать их в логику процесса посредством retries-with-delay или human task. Это сильно усложняет и замусоривает сам процесс. Кроме того, не стоит путать асинхронные вызовы с message-oriented communication. Асинхронные вызовы можно сделать при помощи того же синхронного вебсервиса, используя паттерны InvokeAsync и InvokeWithCallback. Во втором случае нужен сторонний брокер, который берет на себя задачи reliable delivery и доступен приложению в режиме 24/7.


Инкапсуляция бизнес-логики в микросервисах

Заведомо плохая идея выносить что-то, что зависит от состояния вне транзакции. После чтения данных из микросервиса сразу же теряется их актуальность. Контролировать целостность в таких случаях приходится вручную с лишними приседаниями: дополнительные операции компенсации, distributed locking (тот еще геморрой). Здесь не стоит гнаться за модой и на ровном месте создавать себе проблемы — микросервисы придуманы поколением девопсеров, которые по большей части произошли из мира JS, и большинство из которых плохо понимают концепции race conditions и acid. Оркестрация нужна там, где ваш процесс выходит за рамки одной системы (и собственно одного хранилища). В этом случае их нельзя объединить одной транзакцией.


Все сообщения – в одной очереди

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


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

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


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

Это отдельная тема для лютого геморроя, где не существует стандартного хорошего решения. Большинство коммерческих BPMS имеют проприетарный формат хранения данных, поэтому вариант 2 отпадает. Некоторые BPMS продают на презентациях умение версионировать, но это касается только самого flow: естественно ни состояние ни интерфейсы, ни логику ни сервисы никто не версионирует. Кроме того, многие BPMS позволяют деплоить версии ранзных процессов, но не дают версионировать java-бандлы, которые идут вместе с ними, и где есть львиная доля всей логики. Есть еще 3 вариант: откатить запущенные процессы и рестартнуть их в новой версии. Актуально для коротких процессов, где определена компенсация каждого из них.

Большое спасибо за развернутые комментарии! Они отлично дополняют статью.


Версионирование java-бандлов в Drools и jBPM реально очень помогает. Мы уже давно используем их kie-модули, и это позволяет нам очень быстро вносить мелкие правки.
Сейчас мы умеем версионировать и загружать java-бандлы уже без использования технологий RedHat, и новые бизнес-процессы на нашем фреймворке разрабатываем именно в виде таких бандлов, хотя BPM и бандлы — это два совершенно независимых решения.
Но мы пошли еще дальше, и начали загружать в контейнер бандла не только jar с самими бизнес-процессами, но и jar c имплементацией соответствующего DSL. Это способствует дальнейшей эволюции способов задания бизнес-процессов, не ограничиваясь представленным в статье вариантом. То есть в работающем приложении у меня может быть загружено два бандла с бизнес-процессами, реализованные принципально различными способами. Или же, два бандла с использованием несовместимых между собой версий DSL-библиотеки.


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

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

Может быть, я тут не до конца мысль оформил. Имелось ввиду не то, что бизнес-логика процесса будет обрабатывать события параллельно, а что запущена обработка будет в параллельных потоках, которые конкурируют за возможность изменения внутреннего состояния экземпляра бизнес-процесса. До момента синхронизации потоков сообщение сначала будет взято из очереди, десериализовано, будет сделан запрос в базу для нахождения экземпляров бизнес-процессов, которые на это сообщение подписаны. Это тоже в некотором смысле обработка :)

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

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


  1. Отзывчивость системы падает с ростом объема функциональности и/или данных, иногда пользователи не могут дождаться ответа от системы, даже при низкой загрузке сервера. Возможно, бизнес-логика системы пытается синхронно выполнять тяжелую обработку данных, которую было бы правильнее выполнять асинхронно. Тогда на сервере появляется некий процесс, в котором чередуются действия пользователей и машинная обработка данных, но появляется спектр проблем, для решения которых может применяться описанные в статье подходы. Но бизнес в данном случае должен быть готов к кардинальным переменам в части UI/UX.
  2. В какой-то момент бизнес приходит к необходимости оптимизации своих бизнес-процессов, но для этого нужны статистические данные для анализа, чтобы выявить узкие места. Нет необходимости покупать и внедрять очень дорогие BPMS, если бизнес-логика рассматриваемых процессов уже заложена в программные системы, и реализована в общем стиле, в терминах активностей и переходов между ними. Для анализа достаточно просто иметь общее хранилище логов прохождения процессов во всех компонентах системы, в формализованном виде. Моя статья делает упор на то, что интеграционная логика тоже должна оформляться в таком же стиле, а значит интеграционные издержки тоже будут видны при анализе (а они могут вносить очень существенный вклад в общее время прохождения бизнес-процесса).
  3. С ростом размера системы увеличивается время внесения доработок, время выпуска новой версии системы в несколько раз превышает время работы разработчика над задачей. Разворачивание новой версии в продакшене требует остановки большого количества функциональности на ощутимое время. Возможная причина — появление слишком большого монолита в составе системы, тестирование и стабилизация которого занимает очень продолжительное время, и создает проблемы с обновлением. Если такое уже произошло, то у бизнеса большая проблема — решить ее будет очень дорого. Правильнее было бы изначально не допускать разрастания компонента системы в очень крупный монолит, а для этого потребуются качественные интеграционные механизмы, чтобы связь между более мелкими компонентами системы была бесшовной и незаметной для пользователей.
Sign up to leave a comment.