GitHub - berndruecker/trip-booking-saga-java:使用輕量級開源工作流引擎(Camunda) 實現Saga模式的示例

banq發表於2019-07-30

Saga模式描述瞭如何在沒有兩階段提交2PC的情況下解決分散式(業務)事務,因為2PC不能在分散式系統中擴充套件。Saga模式基本思路是將整個交易分解為多個步驟或活動。只有內部的步驟可以在原子事務中執行,但整體的一致性由Saga處理。Saga有責任完成整個業務交易或使系統處於已知的終止狀態。因此,如果出現錯誤,則應用業務回滾過程,該過程通過以相反順序呼叫補償步驟或活動來實現。可以更詳細地瞭解Sagas :如何在沒有兩階段提交的情況下實現複雜的業務交易

在該Github示例的酒店案例中(點選標題),汽車和航班預訂可能由不同的遠端服務完成。所以沒有技術事務,而只有業務事務。如果無法成功進行航班預訂,您需要取消酒店和汽車。

GitHub - berndruecker/trip-booking-saga-java:使用輕量級開源工作流引擎(Camunda) 實現Saga模式的示例

使用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視覺化工具。

GitHub - berndruecker/trip-booking-saga-java:使用輕量級開源工作流引擎(Camunda) 實現Saga模式的示例

相關專案:

Uber優步使用上述架構的案例:cadence-java-samples

相關文章