Saga模式描述了如何在没有两阶段提交的情况下解决分布式(业务)事务,因为这不能在分布式系统中扩展。基本思路是将整个交易分解为多个步骤或活动。只有内部的步骤可以在原子事务中执行,但整体的一致性由Saga处理。Saga有责任完成整个业务交易或使系统处于已知的终止状态。因此,如果出现错误,则应用业务回滚过程,该过程通过以相反顺序调用补偿步骤或活动来实现。可以更详细地了解Sagas :如何在没有两阶段提交的情况下实现复杂的业务交易
在该Github示例的酒店案例中(点击标题),汽车和航班预订可能由不同的远程服务完成。所以没有技术事务,而只有商业事务。如果无法成功进行航班预订,您需要取消酒店和汽车。
使用 Camunda, 您可以使用图形建模或称为Model-API的Java DSL来实现Saga。由于Camunda非常轻量级,您可以启动所谓的流程引擎,定义Saga并通过几行Java代码运行实例(如果使用默认配置和内存中的H2数据库),请参阅 TripBookingSaga.java :
<b>public</b> <b>class</b> TripBookingSaga { <b>public</b> <b>static</b> <b>void</b> main(String args) { <font><i>// Configure and startup (in memory) engine</i></font><font> ProcessEngine camunda = <b>new</b> StandaloneInMemProcessEngineConfiguration() .buildProcessEngine(); </font><font><i>// define saga as BPMN process</i></font><font> ProcessBuilder saga = Bpmn.createExecutableProcess(</font><font>"trip"</font><font>); </font><font><i>// - flow of activities and compensating actions</i></font><font> saga.startEvent() .serviceTask(</font><font>"car"</font><font>).name(</font><font>"Reserve car"</font><font>).camundaClass(ReserveCarAdapter.<b>class</b>) .boundaryEvent().compensateEventDefinition().compensateEventDefinitionDone() .compensationStart().serviceTask(</font><font>"car-compensate"</font><font>).name(</font><font>"Cancel car"</font><font>).camundaClass(CancelCarAdapter.<b>class</b>).compensationDone() .serviceTask(</font><font>"hotel"</font><font>).name(</font><font>"Book hotel"</font><font>).camundaClass(BookHotelAdapter.<b>class</b>) .boundaryEvent().compensateEventDefinition().compensateEventDefinitionDone() .compensationStart().serviceTask(</font><font>"hotel-compensate"</font><font>).name(</font><font>"Hotel car"</font><font>).camundaClass(CancelCarAdapter.<b>class</b>).compensationDone() .serviceTask(</font><font>"flight"</font><font>).name(</font><font>"Book flight"</font><font>).camundaClass(BookFlightAdapter.<b>class</b>) .boundaryEvent().compensateEventDefinition().compensateEventDefinitionDone() .compensationStart().serviceTask(</font><font>"flight-compensate"</font><font>).name(</font><font>"Cancel flight"</font><font>).camundaClass(CancelCarAdapter.<b>class</b>).compensationDone() .endEvent(); </font><font><i>// - trigger compensation in case of any exception (other triggers are possible)</i></font><font> saga.eventSubProcess() .startEvent().error(</font><font>"java.lang.Throwable"</font><font>) .intermediateThrowEvent().compensateEventDefinition().compensateEventDefinitionDone() .endEvent(); </font><font><i>// finish Saga and deploy it to Camunda</i></font><font> camunda.getRepositoryService().createDeployment() </font><font><i>//</i></font><font> .addModelInstance(</font><font>"trip.bpmn"</font><font>, saga.done()) </font><font><i>//</i></font><font> .deploy(); </font><font><i>// now we can start running instances of our saga - its state will be persisted</i></font><font> camunda.getRuntimeService().startProcessInstanceByKey(</font><font>"trip"</font><font>, Variables.putValue(</font><font>"name"</font><font>, </font><font>"trip1"</font><font>)); camunda.getRuntimeService().startProcessInstanceByKey(</font><font>"trip"</font><font>, Variables.putValue(</font><font>"name"</font><font>, </font><font>"trip2"</font><font>)); } } </font>
真正业务逻辑在适配器类,如 BookHotelAdapter .
上述定义可能看起来有点冗长,因为您必须使用BPMN术语。但是你可以编写一个瘦的 SagaBuilder 来提高Saga定义的可读性:
SagaBuilder saga = SagaBuilder.newSaga(<font>"trip"</font><font>) .activity(</font><font>"Reserve car"</font><font>, ReserveCarAdapter.<b>class</b>) .compensationActivity(</font><font>"Cancel car"</font><font>, CancelCarAdapter.<b>class</b>) .activity(</font><font>"Book hotel"</font><font>, BookHotelAdapter.<b>class</b>) .compensationActivity(</font><font>"Cancel hotel"</font><font>, CancelHotelAdapter.<b>class</b>) .activity(</font><font>"Book flight"</font><font>, BookFlightAdapter.<b>class</b>) .compensationActivity(</font><font>"Cancel flight"</font><font>, CancelFlightAdapter.<b>class</b>) .end() .triggerCompensationOnAnyError(); camunda.getRepositoryService().createDeployment() .addModelInstance(saga.getModel()) .deploy(); </font>
引擎将负责状态处理,补偿,还可以处理超时和升级。
在现实场景中,您可以以不同方式配置和运行Camunda引擎,例如使用Spring或Spring Boot。在这个例子中,您还可以使用 Spring Boot应用程序 来 启动应用 程序 - 之后甚至可以连接Camundas可视化工具。