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