понедельник, 11 июня 2012 г.

Длительно выполняющиеся транзакции в BPEL. Механизм компенсаций

Язык Business Process Modeling and Execution Language (BPEL, читается «БИПЛЬ») предназначен для моделирования бизнес-процессов предприятия посредством оркестровки сервисов. При этом, как исполнение самих операции сервисов, так и принятие решения о том, какой именно сервис вызвать, могут занимать довольно длительное время: часы, дни, недели. Особо характерна большая длительность операций в том случае, если они выполняются пользователями.

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


Механизм компенсаций в BPEL состоит из двух частей: обработчика компенсаций и действия Compensate. Обработчик компенсаций создается для блока (Scope) или (актуально для BPEL 1.1) всего процесса. При выполнении действия Compensate запускаются обработчики компенсаций успешно выполненных вложенных блоков того блока, в котором присутствует данное действие Compensate. При этом выполнение блока считается завершенным успешно, даже если ошибка возникла, но была перехвачена в обработчике ошибок (catch или catchAll) данного блока. Если необходимо вызвать обработчики компенсаций у дочерних блоков, то нужно разместить действие Compensate в обработчике компенсаций родительского блока. Важно: действие Compensate может быть размещено только в обработчике ошибок или в обработчике компенсаций.

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



В BPEL 2.0 у действия Compensate убран параметр Scope. Вместо действия Compensate с параметром Scope добавлено действие CompensateScope с параметром Target. Правила размещения действия CompensateScope аналогичны правилам размещения действия Compensate.



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

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

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

Общая схема процесса выглядит следующим образом:



При этом все три блока однотипны и для понимания их структуры достаточно рассмотреть блок покупки билета на самолет:



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

На рисунке видно, что обращение к сервисам HotelBookingService и AirlinesService выполнено по два раза: один раз для покупки соответствующей услуги и один раз для отмены.



При трассировке исполнения бизнес-процесса видно, что для блоков TicketBookingScope и HotelBookingScope сработали обработчики компенсаций (compensationHandler).



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

Измененная схема процесса выглядит следующим образом:



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

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



При трассировке исполнения бизнес-процесса видно, что для блока TicketBookingScope сработал обработчик компенсаций (compensationHandler).



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

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

Понравилось сообщение - подпишитесь на блог и Twitter

5 комментариев:

Lekseich комментирует...

Интересно. Но хотелось бы увидеть пример от начала до конца какого-нибудь маленького проекта с использованием BPEL.
Чтобы не разрозненно, а прямо вот сначала создаем сервисы, потом создаем workflow, а потом смотрим что получилось.
Порог вхождения в BPEL достаточно высок, поэтому хотелось бы увидеть хотя бы одну такую законченную от и до статью
Заранее спасибо :)

Pavel Samolisov комментирует...

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

В общем обещать не буду, но тот факт, что сквозной пример нужен, зафиксирую.

Lekseich комментирует...

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

Seomazzi комментирует...

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

Pavel Samolisov комментирует...

@Seomazzi
Как вы просили. Координация родительских и дочерних BPEL-процессов. Сигналы.

Отправить комментарий

Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!