[마이크로서비스 패턴 쉽게 개발 1] 마이크로서비스 패턴 이해: Saga, Event Sourcing, API Composition, CQRS
마이크로 서비스 패턴 중 이번 실습에 적용되는 패턴인 Saga, Event Sourcing, API Composition, CQRS(Command Query Responsibility Segregation)에 대해 기본적인 이해를 먼저 해 보겠습니다.
시간이 부족한 분들을 위해 핵심만 설명하겠습니다. 글로 이해가 잘 안되실 수 있는데, 이후 실습하면서 다시 한번 보시면 조금 더 명확히 이해가 되실 겁니다.
보다 자세한 이론적 설명은 제가 예전에 쓴 아래 글을 참고하세요.
https://happycloud-lee.tistory.com/154
Saga 패턴
Saga 패턴의 목적은 마이크로서비스별로 "분산된 DB간의 데이터 정합성을 맞추는 것"입니다.
동작 원리는 '일단 실행하고 문제 있으면 Rollback한다'입니다. 송금 서비스에 Saga 패턴이 적용된 모습을 단순하고 좀 극단적으로 표현하면 아래와 같습니다.
Pivot transaction인 'plusMoneyInReceiver'가 실패하면 각 서비스의 Compensating transaction이 실행되어 DB간 데이터 정합성을 맞춥니다. Repeatable transaction인 'pushMessage'는 실패 시 계속 반복 수행하여 결국 처리가 되도록 합니다.
이러한 일련의 프로세스는 서비스와 서비스가 직접 통신하지 않고 중간에 MQ(Message Queue)를 두어 비동기 통신을 합니다.
예를 들어 송금서비스에서 'minusMoneyInSender(1000원)'이라는 Transaction을 실행한 후 '1000원을 수금하라'라는 요청 메시지를 만들어 MQ에 보냅니다. 수금 서비스는 이러한 요청 메시지가 쌓이는 MQ의 채널을 구독하고 있습니다. 그래서, 이 구독한 채널에 요청 메시지가 오면 그것을 Push받습니다. Push받은 메시지의 데이터를 이용하여 'plusMoneyInReceiver' transaction을 실행하게 됩니다.
마이크로서비스 | Transaction | Compensating Transaction | |
송금 서비스 | checkAccount(1000원): 잔고 체크 | X | |
Compensatable Transaction |
minusMoneyInSender(1000원): 발신자 계좌에서 1000원을 뺌 | plusMoneyInSender(1000원) | |
수금 서비스 | Pivot Transaction |
plusMoneyInReceiver(1000원): 수신자 계좌에 1000원을 더함 | minusMoneyInReceiver(1000원) |
알림 서비스 | Repeatable Transaction |
pushMessage(발신자) pushMessage(수신자) |
X |
단순한 프로세스는 위 예와 같이 각 서비스가 자기가 필요한 채널을 구독하여 수신 받은 메시지에 따라 적절한 처리를 하고 결과 메시지를 MQ에 보내는 방식을 사용합니다. 이렇게 각 서비스가 자율적으로 Transaction을 처리하는 방식을 Choreography(자유연출)방식이라 합니다.
비동기 통신이기 때문에 참여하는 마이크로서비스와 Transaction의 수가 많아 질 수록 점점 더 복잡해 집니다.
그래서 중앙의 콘트롤 타워가 프로세스를 통제하는 Orchestrated방식이 필요하게 됩니다.
이번 실습에서 사용할 Axon Framework은 Orchestrated 방식 Saga를 지원 합니다.
Event Sourcing 패턴
Event Sourcing 패턴의 목적은 RDB의 'Atomic(원자성)'을 보장하는 것입니다.
원자성이란 여러 테이블에 데이터를 Write할 때 어느 한 테이블에 데이터 Write가 실패한다면 전체 Transaction을 취소시키고, 모두 문제가 없다면 전체 테이블에 데이터를 Write하는 특성을 말합니다. 즉, 'All or Nothing'을 보장하는 특성입니다.
마이크로서비스 간 비동기 통신에서는 최소한 2개의 테이블이 필요합니다. 비즈니스 데이터를 담고 있는 테이블(예: 주문, 배송 등)과 MQ에 보낼 메시지를 담고 있는 테이블(Transaction Outbox 테이블) 이죠. 이 두 테이블이 RDB라면 '원자성' 특성을 이용하여 두 테이블에 모두 안전하게 데이터를 Write할 수 있습니다. 하지만 NoSQL DB를 사용한다면 '원자성'을 보장할 수 가 없습니다.
이를 해결하기 위한 Event Sourcing 패턴의 동작 원리는 물리적인 테이블에 데이터를 저장하는 대신에 발생하는 Event를 처음부터 다시 Replay하여 최종 데이터를 계산하는 것입니다. 이때 Event를 축적하는 저장소를 Event Store라고 합니다.
아래와 같이 Event Store에 Event가 저장되어 있는데 'Event #5'가 주문 서비스에 요청 되었다고 생각해 봅시다.
그럼 ORDER01번에 해당하는 #1, #3, #4번을 Event Replay하여 최종 상태인 '아메리카노 2잔, 오렌지 주스 2잔, 콜라 1잔'을 계산합니다. #5번째 Event가 오렌지 주스 1잔과 콜라 1잔 취소이므로 최종 데이터 상태는 '아메리카노 2잔, 오렌지 주스 1잔'이 됩니다. Event Replay는 모든 이벤트가 아니라 Primary Key에 해당하는 Event만 Replay합니다.
Event Sourcing 패턴을 사용하면 비즈니스 데이터를 담고 있는 테이블이 필요 없습니다. MQ에 발행할 Event만 별도 테이블에 담거나 Event Replay를 처리하는 객체에서 바로 발송하면 됩니다.
이렇게 최종 데이터 상태를 Event Replay로 구하고 MQ에 메시지를 발송하며 Event Store에 Event를 저장하는 객체를 Aggregate라고 합니다.
주문ID | Event 종류 | Event | 최종 데이터 상태 |
ORDER01 | 신규주문 | #1: 아메리카노 2잔, 오렌지 주스 3잔 | 아메리카노 2잔, 오렌지 주스 3잔 |
ORDER02 | 신규주문 | #2: 카페라떼 1잔 | 카페라떼 1잔 |
ORDER01 | 주문수정 | #3: 오렌지 주스 1잔 취소 | 아메리카노 2잔, 오렌지 주스 2잔 |
ORDER01 | 주문수정 | #4: 콜라 1잔 추가 | 아메리카노 2잔, 오렌지 주스 2잔, 콜라 1잔 |
ORDER01 | 주문수정 | #5: 오렌지 주스 1잔, 콜라 1잔 취소 | ? |
위 주문 서비스의 예에서는 동일 주문에 대해 발생하는 Event수가 그리 많지 않아 Event Replay시간이 많이 걸리지는 않을 겁니다.
그런데 '통장계좌'서비스를 생각해 봅시다. 통장을 개설하고 각 통장마다 발생하는 Event수는 엄청나게 많을 것입니다.
예를 들어 한 통장에 1달에 보통 100건의 입출금 Event가 발생했고 10년(120개월)을 사용했다면 총 Event의 수는 12,000건이 됩니다.
12,001번째 입금 Event가 발생했을때 최종 잔금을 계산하려면 12,000번의 Event Replay를 해야 되겠죠.
그래서 Event Replay의 성능을 향상시키기 위해 'Snapshot'을 사용합니다.
일정 Event 갯수나 일정 시간마다 최종 데이터 상태를 'Snapshot'저장소에 기록하고, 그 이후 발생한 Event들만 Replay를 하는 것입니다. 위 통장의 예에서 10건마다 Snapshot을 저장했다면 최대 Replay할 Event의 갯수는 10개밖에 안될것입니다.
Event Sourcing패턴에서는 또 하나 해결해야 할 문제가 있습니다. 바로 최종 데이터를 빠르게 조회(Query)할 수 있게 해야 한다는 것입니다. 빠른 조회 서비스를 위해서는 물리적인 조회 전용 DB를 구성하는 것이 가장 좋습니다.
따라서 Event Replay를 통해 얻은 데이터의 최종 상태를 조회 전용 DB에 업데이트하는 방법을 사용합니다.
결론적으로 Event Sourcing 패턴을 적용하게 되면 데이터가 Write되는 저장소와 데이터를 Read하는 저장소가 분리되게 됩니다.
다시 말해, 데이터 Write는 Event Store에 하게 되고, 데이터 Read는 조회 전용 DB에서 하게 됩니다.
Data Write 요청을 Command라고 하고 Data Read 요청을 Query라고 합니다.
이렇게 Data Write 처리와 Data Read 처리를 분리하는 패턴을 CQRS(Command Query Responsibility Segregation)이라고 합니다.
Event Sourcing 패턴을 적용할 때는 자연스럽게 CQRS패턴도 적용되게 됩니다.
중요) Aggregate 규칙: RPO Root only: 서비스간 통신 시 Aggregate Root만 참조 - Aggregate내부의 entity나 VO를 직접 접근하지 말고 Aggregate를 통해서만 접근함 Primary key: Aggregate 참조 시 Primary key 이용 - 다른 Aggregate를 참조할때 Entity객체 전체가 아닌 Entity의 primary key를 이용함 - 예) Order서비스에서 '고객번호 001'번 고객 데이터를 참조할 때 Primary Key인 consumerId값을 이용하여 Aggregate를 접근 One to One: 동일 서비스에서 한 개의 트랜잭션은 한 개의 Aggregate만 Writing - 특정 처리가 여러개의 Aggregate를 Writing하면 원자성(All or Nothing: 참여 DB의 Writing이 모두 성공해야 전체DB를 Writing함)을 보장하기 어렵기 때문임 - 예) 신규 주문 처리 시 주문 Aggregate와 고객 Aggregate를 모두 Write 하면 한쪽 Aggregate만 Writing 될 수 있어 데이터 정합성이 깨질 수 있음 |
CQRS 패턴
CQRS패턴의 목적은 '데이터 Write 처리와 데이터 Read 처리를 분리 하는 것'입니다.
보다 실질적인 목적은 마이크로 서비스 DB간 Join이 불가하므로 조회 전용 DB를 구축하여 조회 성능을 향상 시키는 것입니다.
CQRS패턴의 동작 원리는 '각 마이크로서비스 DB의 변경 Event를 MQ로 부터 받아 조회DB 데이터를 갱신하는 것'입니다.
API Composition 패턴
API Composition 패턴의 목적은 '여러 마이크로서비스의 데이터를 조합하여 조회 성능을 향상 시키는 것'입니다.
CQRS패턴을 적용하여 조회 전용 DB가 있다면 그 DB만 조회하면 되므로 API Composition 패턴까지 적용할 필요는 없을 수도 있습니다.
하지만 조회 전용 DB에 누락된 정보가 있다면 상황에 따라 필요할 수 있습니다.
단, 여러건의 데이터를 조회할 때 사용하기 보다는 한 건의 데이터를 조회할 때 적합합니다.
예를 들어 내가 주문한 1건에 대해 주문일자, 주문제품, 결제 시간, 결제 금액, 배송 상태등을 한 페이지에서 보여 줘야 한다고 합시다.
주문, 결제, 배송 서비스가 각각의 DB를 갖고 있으므로 3번의 API 호출이 필요할 것입니다.
각 서비스 앞에 각각의 API 결과를 조합해 주는 서비스나 모듈을 둔다면 Client는 한번만 API호출을 하면 됩니다. API 조합 서비스가 뒷 단의 주문, 결제, 배송 서비스 API를 호출하여 그 결과를 조합하여 리턴해주면 되기 때문입니다.
이렇게 Frontend 어플리케이션을 위해 Backend 어플리케이션과 통신하는 서비스를 BFF(Backend For Frontend)라고 합니다.
이러한 API Composition 역할을 BFF 대신에 API Gateway에서 해 줄 수도 있습니다.
이상으로 실습에 적용될 Saga, Event Sourcing, CQRS, API Composition 패턴에 대해 이해 하였습니다.
다음 장에서는 실습을 위한 환경 준비를 하도록 하겠습니다.
마이크로서비스 패턴 쉽게 개발하기 목차
- 마이크로서비스 패턴 이해: Saga, Event Sourcing, API Composition, CQRS 이해
- 실습환경 준비
- 주문 서비스 테스트
- Axon Framework 이해
아래 주제들은 '마이크로서비스패턴 쉽게 개발하기'라는 제 책에서 만나실 수 있습니다.
- 멀티 모듈 프로젝트 작성
- 신규 주문 정상처리 프로세스 구현
- 배송 상태 변경 및 재고 증감 처리
- 신규 주문 보상처리 프로세스 구현
- 주문 수정 정상처리 프로세스 구현
- 주문 수정 보상처리 프로세스 구현
- 주문 삭제 정상처리 프로세스 구현
- 주문 삭제 보상처리 프로세스 구현
- API Composition 패턴과 CQRS 패턴
책 한번 내겠다는 평소의 꿈을 실현하기 위해 이번에 전자책을 내게 되었습니다.
제 꿈을 응원하신다는 마음으로 전체 내용을 공유하지 않는것을 양해해 주시고
구매까지 해 주시면 더욱 감사하겠습니다.
https://happycloud-lee.tistory.com/notice/291