Spring Boot 마이크로서비스의 Saga 패턴에 대한 실용 가이드
최신 마이크로서비스는 규모가 작고 자율적이며 독립적으로 배포가 가능합니다. 하지만 이 역시 큰 문제를 야기합니다.
일반적인 약국 처방 관리 – 결제, 은행, 물류, 공급망 또는 예약 시스템이 전체 운영을 수행합니다. 여러 마이크로서비스
- 주문 생성
- 재고 예약
- 결제 처리
- 배송물 생성
- 로열티 포인트 업데이트
각각은 자체적으로 실행됩니다. 데이터 베이스, 서비스그리고 네트워크 경계.
하나의 작업이 중간에 실패하더라도 전체 비즈니스 흐름이 시스템을 일관성 없는 상태로 남겨두어서는 안 됩니다.
이곳은 사가 패턴 필수가 됩니다.
사가(Saga)란 무엇입니까? ACID를 사용하지 않는 이유는 무엇입니까?
에이 사거 일련의 로컬 트랜잭션입니다. \n 각 로컬 트랜잭션은 하나의 서비스 내의 데이터를 업데이트하고 이벤트를 게시하여 다음 단계를 트리거합니다.
오류가 발생하는 경우:
- 사가 트리거 보상 조치 이전에 완료된 단계를 취소하려면
ACID(2PC)가 마이크로서비스에서 작동하지 않는 이유는 무엇입니까?
| 문제 | 설명 | |:—:|:—:| | 2PC가 차단 중입니다. | 코디네이터가 충돌하면 모든 것이 중단됩니다. | | 마이크로서비스에는 독립적인 DB가 있습니다. | 분산 DB 전체에 공유 커밋 로그가 없습니다. | | 제대로 확장되지 않음 | 잠금은 분산 시스템에 걸쳐 있습니다. | | 클라우드 서비스는 XA 트랜잭션을 지원하지 않습니다. | DynamoDB, S3, Mongo, Kafka, RDS 등 |
Saga는 이벤트 중심 또는 조정된 보상 논리를 통해 이러한 문제를 방지합니다.
사가 아키텍처 다이어그램:
\

1. 사용 사례: 약국 처방전 주문 처리
대본
상상해 보세요 약국 처방전 온라인 플랫폼 고객이 제품을 주문하는 곳.
시스템에는 세 가지 독립적인 서비스가 포함됩니다.
- 주문 서비스 – 주문기록을 생성합니다.
- 재고 서비스 – 재고가 있는 제품을 예약합니다.
- 결제 서비스 – 고객에게 비용을 청구합니다.
사가가 없는 문제
이러한 단계 중 하나가 실패하면(예: 결제 실패) 이전 단계로 인해 시스템이 일관성 없는 상태가 될 수 있습니다.
- 주문이 생성되었습니다.
- 재고 보유
- 결제 실패
- 결과: 재고가 정체되고 주문이 불완전하며 수동 개입이 필요합니다.
이는 특히 여러 마이크로서비스가 상호 작용하는 경우 엔터프라이즈 시스템에서 허용되지 않습니다.
Saga를 이용한 솔루션
사용하여 사가 오케스트레이션 패턴각 서비스는 해당 서비스를 실행합니다. 현지 거래그리고 실패할 경우, 보상 조치 이전 단계 롤백
- 1단계: 주문 서비스는 주문을 생성합니다.
- 2단계: Inventory Service는 재고를 보유합니다.
- 3단계: 결제 서비스는 고객에게 비용을 청구합니다.
- 실패 처리: 결제가 실패하면 오케스트레이터는 Inventory Service에 전화하여 재고를 해제합니다.
이는 다음을 보장합니다. 비즈니스 수준의 일관성 서비스 전반에 걸쳐.
2. Saga 패턴이 필요한 이유
- 분산 마이크로서비스: 기존 ACID 트랜잭션은 여러 마이크로서비스에 걸쳐 있을 수 없습니다.
- 최종 일관성: 전역 리소스 잠금 없이 일관된 결과를 보장합니다.
- 내결함성: 이후 단계가 실패하면 이전 단계를 자동으로 롤백합니다.
- 클라우드 네이티브: 클라우드 환경에서 확장 가능한 서비스와 원활하게 작동합니다.
느슨한 결합: 각 서비스는 독립적으로 유지되며 API를 통해서만 통신합니다.
3. Saga 이용의 장점
- 신뢰할 수 있음: 보상 트랜잭션은 데이터 불일치를 방지합니다.
- 확장성: 각 서비스는 독립적으로 확장할 수 있습니다. 오케스트레이션은 중앙 집중화되어 있습니다.
- 회복력: 부분적인 실패를 정상적으로 처리합니다.
- 비즈니스 수준의 일관성: 엄격한 데이터베이스 일관성이 아닌 올바른 비즈니스 결과에 중점을 둡니다.
- 모니터링 및 디버깅: 중앙 오케스트레이터는 각 트랜잭션 단계에 대한 명확한 로그를 제공합니다.
4. 스크립트의 단계별 설명
아래는 자세한 연습 Spring Boot Saga 프로젝트의 주요 스크립트 구성 요소 중 하나입니다.
4.1 주문 모델(Order.java)
\
package com.example.orderservice;
public class Order {
private Long id;
private String product;
private String status;
public Order() {
// default constructor (required for Jackson)
}
public Order(Long id, String product, String status) {
this.id = id;
this.product = product;
this.status = status;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
-
목적: 주문을 나타냅니다.
-
비즈니스 로직: 제품 이름, 주문 ID 및 상태(생성됨, 취소됨, 완료됨)를 저장합니다.
4.2 주문 컨트롤러(OrderController.java)
\
package com.example.orderservice;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping
public Order create(@RequestParam("product") String product) {
return new Order(1L, product, "CREATED");
}
@PostMapping("/cancel")
public String cancel() {
return "ORDER_CANCELLED";
}
@PostMapping("/complete")
public String complete() {
return "ORDER_COMPLETED";
}
}
\
-
엔드포인트
-
POST /orders → 주문을 생성합니다.
-
POST /orders/cancel → 주문을 취소합니다(보상에 사용).
-
POST /orders/complete → 주문 완료를 표시합니다.
-
Saga에서 작동하는 이유: 엔드포인트는 간단하고 예측 가능하며 오케스트레이션을 위해 개체 또는 상태 문자열을 반환합니다.
4.3 인벤토리 컨트롤러(InventoryController.java)
\
package com.example.inventoryservice;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/inventory")
public class InventoryController {
@PostMapping("/reserve")
public boolean reserve(@RequestParam("product") String product) {
return !"FAIL".equals(product);
}
@PostMapping("/release")
public void release(@RequestParam("product") String product) {}
}
\
-
목적: 재고가 있는 제품을 예약하고, 결제 실패 시 출고합니다.
-
논리: 성공/실패를 시뮬레이션합니다. 엔터프라이즈 시스템에서는 데이터베이스의 실제 재고를 확인합니다.
\
4.4. 결제 컨트롤러(PaymentController.java)\
package com.example.paymentservice;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/payments")
public class PaymentController {
@PostMapping("/pay")
public boolean pay(@RequestParam("amount") double amount) {
return amount <= 5000;
}
@PostMapping("/refund")
public void refund(@RequestParam double amount) {}
}
\
-
목적: 결제를 처리하고 테스트 실패를 시뮬레이션합니다.
-
Saga에서 작동하는 이유: 오케스트레이터가 보상을 트리거할 수 있도록 성공/실패를 나타내는 부울을 반환합니다.
4.5. 사가 오케스트레이터(SagaController.java)
\
package com.example.sagaorchestrator;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/saga")
public class SagaController {
private final RestTemplate rest = new RestTemplate();
@PostMapping("/order")
public String place(@RequestParam("product") String product, @RequestParam("amount") double amount) {
Object order = rest.postForObject(
" + product,
null,
Object.class
);
if (order == null) {
return "Order creation failed";
}
Boolean inventory = rest.postForObject(
" + product,
null,
Boolean.class
);
if (inventory == null || !inventory) {
return "Inventory failed";
}
Boolean payment = rest.postForObject(
" + amount,
null,
Boolean.class
);
if (payment == null || !payment) {
rest.postForObject(
" + product,
null,
Void.class
);
return "Payment failed, compensated";
}
return "Order completed successfully";
}
}
\
- 흐름 설명
- 주문 생성 → 첫 번째 단계를 진행하려면 성공해야 합니다.
- 재고 예약 → 실패하면 무용담이 중지됩니다.
- 결제 처리 → 실패하면 오케스트레이터는 인벤토리 릴리스(보상)를 트리거합니다.
- 성공 → 성공 메시지를 반환합니다.
엔터프라이즈 지원: 시연하다 명확한 오케스트레이션, 보상, 비즈니스 수준의 일관성.
\
4.6 출력 검증
\
-
성공사례

\
- 결제 실패 사례
\

\
- 재고실패 사례:
\ ** 
5. 강조된 장점
- 내결함성: 실패한 트랜잭션을 자동으로 보상합니다.
- 비즈니스 일관성: 주문 상태와 재고가 항상 일관되게 유지되도록 합니다.
- 확장 가능: 각 서비스는 독립적으로 실행됩니다.
- 클라우드 네이티브 지원: Kubernetes 또는 모든 클라우드 환경에서 실행할 수 있습니다.
- 적용 가능한 기업: 의료, 은행, 데이터 플랫폼의 실제 시스템과 일치합니다.
6. 기업에 이 패턴이 필요한 이유
- 분산 시스템에서는 수동 롤백은 오류가 발생하기 쉽습니다.
- 사가는 보장합니다 자동 복구다운타임 감소.
- 일관성 없는 데이터 방지 다중 서비스 워크플로우에서.
- 비동기 처리를 지원합니다. 성능과 확장성을 향상시킵니다.
\



Post Comment