이전 게시물에서에서 분산 트랜잭션 구현의 몇 가지 어려움과 이벤트/안무 접근법을 사용해 사가 패턴을 구현하는 방법을 살펴봤습니다. 이 글에서는 명령 또는 오케스트레이션이라는 또 다른 유형의 사가 구현을 사용해 복잡한 트랜잭션이나 이벤트의 주기적 종속성과 같은 몇 가지 문제를 해결하는 방법에 대해 설명해 보겠습니다.
사가의 명령/오케스트레이션 시퀀싱 로직
오케스트레이션 접근 방식에서는 각 참가자에게 언제 무엇을 해야 하는지 알려주는 전적인 책임이 있는 새로운 서비스를 정의합니다. 그리고 사가 패턴 오케스트레이터는 명령/응답 스타일로 각 서비스와 통신하여 어떤 작업을 수행해야 하는지 알려줍니다.
이전 이커머스 예시를 통해 어떤 모습인지 살펴보겠습니다:
- 주문 서비스 가 보류 중인 주문을 저장하고 주문 사가 오케스트레이터(OSO)에 시작을 요청합니다. 주문 거래 생성.
- OSO 는 결제 실행 명령을 결제 서비스를 입력하면 결제 실행됨 메시지
- OSO 를 보내면 주문 준비 명령을 재고 서비스에 보내면 다음과 같이 응답합니다. 주문 준비 완료 메시지
- OSO 를 보내면 주문 배송 명령을 배달 서비스에 보내면 배달 서비스는 주문 배송 메시지
위의 경우, 오더 사가 오케스트레이터는 "주문 생성" 트랜잭션을 실행하는 데 필요한 흐름이 무엇인지 알고 있습니다. 실패할 경우 각 참가자에게 이전 작업을 취소하라는 명령을 전송하여 롤백을 조정하는 역할도 담당합니다.
사가 오케스트레이터를 모델링하는 표준 방법은 각 변환이 명령 또는 메시지에 해당하는 상태 머신입니다. 상태 머신은 구현하기 쉽고 특히 테스트에 적합하기 때문에 잘 정의된 동작을 구조화하는 데 탁월한 패턴입니다.
사가의 명령/오케스트레이션에서 롤백하기
모든 것을 조정할 오케스트레이터가 있으면 롤백이 훨씬 쉬워집니다:
- 재고 서비스 는 OSO에게 품절 메시지;
- OSO는 트랜잭션이 실패했음을 인식하고 롤백을 시작합니다.
- 이 경우 장애가 발생하기 전에 단일 작업만 성공적으로 실행되었으므로 OSO 를 보내면 환불 클라이언트 명령을 결제 서비스 를 클릭하고 주문 상태를 실패로 설정합니다.
사가 사용의 장점과 단점 명령/오케스트레이션 설계
오케스트레이션 기반 사가에는 다양한 이점이 있습니다:
- 서비스 간의 주기적 종속성을 피하십시오. 사가 오케스트레이터가 사가 참가자를 호출하지만 참가자가 오케스트레이터를 호출하지 않는 경우
- 분산 트랜잭션의 오케스트레이션 중앙 집중화
- 참가자는 명령을 실행/응답하기만 하면 되므로 복잡성을 줄일 수 있습니다.
- 더 쉬운 구현 및 테스트
- 새로운 단계가 추가될 때 트랜잭션 복잡성은 선형적으로 유지됩니다.
- 롤백 관리가 더 쉬워졌습니다.
- 동일한 대상 오브젝트를 변경하려는 두 번째 트랜잭션이 있는 경우, 첫 번째 트랜잭션이 종료될 때까지 오케스트레이터에서 쉽게 보류할 수 있습니다.
그러나 이 접근 방식에는 여전히 몇 가지 단점이 있는데, 그 중 하나는 오케스트레이터에 너무 많은 로직이 집중되어 스마트 오케스트레이터가 멍청한 서비스에게 무엇을 해야 하는지 지시하는 아키텍처로 끝날 위험이 있다는 점입니다.
사가 오케스트레이션 기반의 또 다른 단점은 추가 서비스를 관리해야 하므로 인프라 복잡성이 약간 증가한다는 점입니다.
사가 패턴 팁
트랜잭션당 고유 ID 생성
각 거래에 고유 식별자를 부여하는 것은 추적성을 위한 일반적인 기술이지만, 참여자들이 서로에게 데이터를 요청할 수 있는 표준 방법을 갖추는 데도 도움이 됩니다. 예를 들어, 거래 ID를 사용하면 배송 서비스에서 재고 서비스에 제품 수령 장소를 요청하고 결제 서비스에 주문이 결제되었는지 다시 확인할 수 있습니다.
명령 내에서 답장 주소 추가하기
참가자가 고정된 주소로 답장하도록 설계하는 대신 메시지 내에서 답장 주소를 보내면 참가자가 여러 오케스트레이터에게 답장할 수 있습니다.
무력한 작업
서비스 간 통신을 위해 큐를 사용하는 경우(예: SQS, Kafka, RabbitMQ 등) 개인적으로 작업을 무력화할 것을 권장합니다. 이러한 대기열의 대부분은 동일한 메시지를 두 번 전달할 수 있습니다.
또한 서비스의 내결함성을 높일 수도 있습니다. 클라이언트의 버그로 인해 원치 않는 메시지가 트리거/재생되어 데이터베이스가 엉망이 되는 경우가 종종 있습니다.
동기식 통신 피하기
트랜잭션이 진행됨에 따라 각 작업이 실행되는 데 필요한 모든 데이터를 메시지에 추가하는 것을 잊지 마세요. 전체 목표는 더 많은 데이터를 요청하기 위해 서비스 간에 동기식 호출을 피하는 것입니다. 이렇게 하면 다른 서비스가 오프라인 상태일 때에도 서비스가 로컬 트랜잭션을 실행할 수 있습니다.
단점은 각 단계의 요청/응답을 조작해야 하기 때문에 오케스트레이터가 약간 더 복잡해지므로 장단점을 잘 파악해야 한다는 것입니다.
질문이 있으시면 언제든지 다음 주소로 문의해 주세요. @deniswsrosa
아주 좋아요!
구현이 있나요?
다음은 상태 머신을 구현하는 대안입니다: https://github.com/bertilmuth/requirementsascode. 여러분의 의견을 알려주시면 기쁩니다.
안녕하세요, 시각화에서 메시지 브로커와 채널을 사용하셨습니다. 따라서 세이지 게시가 채널을 중개하는 것처럼 보입니다. 그리고 예를 들어 결제 구독자가 해당 채널을 수행하고 해당 메시지를 수신합니다. 그 후 Saga의 다른 채널로 결과를 보내야 합니다. 그래서 PUB/SUB MQ입니다. 따라서 결제 서비스가 오프라인 상태라면, 사가는 응답에 대한 타임아웃이 있는 로직을 제공해야 합니다. 그렇다면 왜 이 경우 REQ/REP(요청/응답 MQ 모델), 즉 결제 서비스에 직접 요청하는 방식을 사용하지 않을까요? 그러면 응답과 서비스 가용성을 즉시 알 수 있을까요?