이전 게시물에서에서 분산 트랜잭션 구현의 몇 가지 어려움과 이벤트/안무 접근법을 사용해 사가 패턴을 구현하는 방법을 살펴봤습니다. 이 글에서는 명령 또는 오케스트레이션이라는 또 다른 유형의 사가 구현을 사용해 복잡한 트랜잭션이나 이벤트의 주기적 종속성과 같은 몇 가지 문제를 해결하는 방법에 대해 설명해 보겠습니다.

 

사가의 명령/오케스트레이션 시퀀싱 로직

오케스트레이션 접근 방식에서는 각 참가자에게 언제 무엇을 해야 하는지 알려주는 전적인 책임이 있는 새로운 서비스를 정의합니다. 그리고 사가 패턴 오케스트레이터는 명령/응답 스타일로 각 서비스와 통신하여 어떤 작업을 수행해야 하는지 알려줍니다.

이전 이커머스 예시를 통해 어떤 모습인지 살펴보겠습니다:

Command/Orchestration flow

  1. 주문 서비스 가 보류 중인 주문을 저장하고 주문 사가 오케스트레이터(OSO)에 시작을 요청합니다. 주문 거래 생성.
  2. OSO결제 실행 명령을 결제 서비스를 입력하면 결제 실행됨 메시지
  3. OSO 를 보내면 주문 준비 명령을 재고 서비스에 보내면 다음과 같이 응답합니다. 주문 준비 완료 메시지
  4. OSO 를 보내면 주문 배송 명령을 배달 서비스에 보내면 배달 서비스는 주문 배송 메시지

위의 경우, 오더 사가 오케스트레이터는 "주문 생성" 트랜잭션을 실행하는 데 필요한 흐름이 무엇인지 알고 있습니다. 실패할 경우 각 참가자에게 이전 작업을 취소하라는 명령을 전송하여 롤백을 조정하는 역할도 담당합니다.

사가 오케스트레이터를 모델링하는 표준 방법은 각 변환이 명령 또는 메시지에 해당하는 상태 머신입니다. 상태 머신은 구현하기 쉽고 특히 테스트에 적합하기 때문에 잘 정의된 동작을 구조화하는 데 탁월한 패턴입니다.

 

사가의 명령/오케스트레이션에서 롤백하기

모든 것을 조정할 오케스트레이터가 있으면 롤백이 훨씬 쉬워집니다:

  1. 재고 서비스 는 OSO에게 품절 메시지;
  2. OSO는 트랜잭션이 실패했음을 인식하고 롤백을 시작합니다.
    1. 이 경우 장애가 발생하기 전에 단일 작업만 성공적으로 실행되었으므로 OSO 를 보내면 환불 클라이언트 명령을 결제 서비스 를 클릭하고 주문 상태를 실패로 설정합니다.

 

사가 사용의 장점과 단점 명령/오케스트레이션 설계

오케스트레이션 기반 사가에는 다양한 이점이 있습니다:  

  • 서비스 간의 주기적 종속성을 피하십시오. 사가 오케스트레이터가 사가 참가자를 호출하지만 참가자가 오케스트레이터를 호출하지 않는 경우
  • 분산 트랜잭션의 오케스트레이션 중앙 집중화
  • 참가자는 명령을 실행/응답하기만 하면 되므로 복잡성을 줄일 수 있습니다.
  • 더 쉬운 구현 및 테스트
  • 새로운 단계가 추가될 때 트랜잭션 복잡성은 선형적으로 유지됩니다.
  • 롤백 관리가 더 쉬워졌습니다.
  • 동일한 대상 오브젝트를 변경하려는 두 번째 트랜잭션이 있는 경우, 첫 번째 트랜잭션이 종료될 때까지 오케스트레이터에서 쉽게 보류할 수 있습니다.

그러나 이 접근 방식에는 여전히 몇 가지 단점이 있는데, 그 중 하나는 오케스트레이터에 너무 많은 로직이 집중되어 스마트 오케스트레이터가 멍청한 서비스에게 무엇을 해야 하는지 지시하는 아키텍처로 끝날 위험이 있다는 점입니다.

사가 오케스트레이션 기반의 또 다른 단점은 추가 서비스를 관리해야 하므로 인프라 복잡성이 약간 증가한다는 점입니다.

사가 패턴 팁

트랜잭션당 고유 ID 생성

각 거래에 고유 식별자를 부여하는 것은 추적성을 위한 일반적인 기술이지만, 참여자들이 서로에게 데이터를 요청할 수 있는 표준 방법을 갖추는 데도 도움이 됩니다. 예를 들어, 거래 ID를 사용하면 배송 서비스에서 재고 서비스에 제품 수령 장소를 요청하고 결제 서비스에 주문이 결제되었는지 다시 확인할 수 있습니다.

명령 내에서 답장 주소 추가하기

참가자가 고정된 주소로 답장하도록 설계하는 대신 메시지 내에서 답장 주소를 보내면 참가자가 여러 오케스트레이터에게 답장할 수 있습니다.

무력한 작업

서비스 간 통신을 위해 큐를 사용하는 경우(예: SQS, Kafka, RabbitMQ 등) 개인적으로 작업을 무력화할 것을 권장합니다. 이러한 대기열의 대부분은 동일한 메시지를 두 번 전달할 수 있습니다.

또한 서비스의 내결함성을 높일 수도 있습니다. 클라이언트의 버그로 인해 원치 않는 메시지가 트리거/재생되어 데이터베이스가 엉망이 되는 경우가 종종 있습니다.

동기식 통신 피하기

트랜잭션이 진행됨에 따라 각 작업이 실행되는 데 필요한 모든 데이터를 메시지에 추가하는 것을 잊지 마세요. 전체 목표는 더 많은 데이터를 요청하기 위해 서비스 간에 동기식 호출을 피하는 것입니다. 이렇게 하면 다른 서비스가 오프라인 상태일 때에도 서비스가 로컬 트랜잭션을 실행할 수 있습니다.

단점은 각 단계의 요청/응답을 조작해야 하기 때문에 오케스트레이터가 약간 더 복잡해지므로 장단점을 잘 파악해야 한다는 것입니다.

질문이 있으시면 언제든지 다음 주소로 문의해 주세요. @deniswsrosa

작성자

게시자 데니스 로사, 개발자 옹호자, 카우치베이스

데니스 로사는 독일 뮌헨에 거주하고 있는 카우치베이스의 개발자 옹호자입니다. 그는 소프트웨어 엔지니어로서 탄탄한 경력을 쌓았으며 Java, Python, Scala, Javascript를 유창하게 구사합니다. Denis는 검색, 빅 데이터, AI, 마이크로서비스 및 개발자가 아름답고 빠르고 안정적이며 확장 가능한 앱을 만드는 데 도움이 되는 모든 것에 대해 글을 쓰는 것을 좋아합니다.

댓글 하나

    1. 다음은 상태 머신을 구현하는 대안입니다: https://github.com/bertilmuth/requirementsascode. 여러분의 의견을 알려주시면 기쁩니다.

  1. 안녕하세요, 시각화에서 메시지 브로커와 채널을 사용하셨습니다. 따라서 세이지 게시가 채널을 중개하는 것처럼 보입니다. 그리고 예를 들어 결제 구독자가 해당 채널을 수행하고 해당 메시지를 수신합니다. 그 후 Saga의 다른 채널로 결과를 보내야 합니다. 그래서 PUB/SUB MQ입니다. 따라서 결제 서비스가 오프라인 상태라면, 사가는 응답에 대한 타임아웃이 있는 로직을 제공해야 합니다. 그렇다면 왜 이 경우 REQ/REP(요청/응답 MQ 모델), 즉 결제 서비스에 직접 요청하는 방식을 사용하지 않을까요? 그러면 응답과 서비스 가용성을 즉시 알 수 있을까요?

댓글 남기기