요약
- 애플리케이션 패턴의 나머지 부분에 대해 이해함.
- 분산 트랜잭션 처리 패턴
- 읽기와 쓰기 분리: CQRS 패턴
- CQRS 패턴은 명령(입력, 수정, 삭제)과 조회 기능을 분리함으로써 성능 향상을 이루고 리소스 교착상태를 해결할 수 있는 방법임.
- 이벤트 메시지 주도 아키텍처와 연계하여 쓰기 모델과 읽기 모델을 분리함으로써 쓰기 시스템의 부하를 줄이고 조회 대기 시간을 줄일 수 있음.
- API 조합과 CQRS
- CQRS는 다양한 마이크로서비스를 연계해서 서비스를 제공하는 경우 API 조합을 통해 하위 서비스의 의존성을 줄이는 데 도움이 됨.
- 주문 이력과 같은 서비스를 제공하는 마이크로서비스는 독자적인 저장소를 갖도록 하여 원천 정보를 보유한 여러 마이크로서비스와 데이터 일관성을 유지할 수 있음.
메모
분산 트랜잭션 처리 패턴
- 위 문제를 해결하기 위한 한 가지 방법으로 여러 개의 분산된 서비스를 하나의 일관된 트랜잭션으로 묶는다.
- 분산 트랜잭션 처리에서는 여러 서비스 간의 비즈니스 및 데이터 및 데이터 일관성을 유지할 필요가 있음.
- 2단계 커밋과 같은 전통적인 기법을 사용하여 일관성을 유지한다.
- 2단계 커밋은 분산 데이터베이스 환경에서 원자성을 보장하기 위해 분산 트랜잭션에 포함돼 있는 모든 노드가 커밋되거나 롤백하는 메커니즘임.
- 이 방법은 각 서비스에 잠금이 걸려 발생하는 성능 문제 때문에 효율적인 방법이 아님.
- 특히, 각 서비스가 다른 인스턴스로 로딩되기 때문에 통제하기 힘듦.
- 서비스의 저장소가 각각 다를 경우 문제가 있음,
- 특히 MongoDB 같은 NoSQL 저장소는 2단계 커밋 자체를 지원하지 않음.
- 특히, 네트워크 장애가 발생하면 서비스가 즉시 영향을 받게됨.
- 이 방법 대신, 마이크로서비스의 독립적인 분산 트랜잭션 처리를 지원하는 패턴이 바로
사가(Saga)패턴
임.
- 각 서비스의 로컬 트랜잭션을 순차적으로 처리하는 패턴임.
- 여러 개의 분산된 서비스를 하나의 트랜잭션으로 묶지 않고 각 로컬 트랜잭션과 보상 트랜잭션을 이용해 비즈니스 및 데이터의 정합성을 맞춘다.
- 다른 트랜잭션의 결과에 따라 롤백이 필요하다면?
- 여기서 나오는 개념이 보상 트랜잭션임.
- 어떤 서비스에서 트랜잭션 처리에 실패할 경우, 그 서비스의 앞선 다른 서비스에서 처리된 트랜잭션을 되돌리게 하는 트랜잭션임.
- p69 주문 처리 고객 신용한도 정보 예시 참고
데이터 일관성에 대한 생각의 전환: 결과적 일관성
- 모든 애플리케이션에는 비즈니스 처리를 위한 규칙이 있고, 이러한 비즈니스 규칙을 만족하도록 데이터 일관성이 유지돼야 함.
- 모든 비즈니스 규칙들이 실시간으로 일관성을 맞춰야 하는가?
- p70 주문 결제 이메일 서비스 예제 참고
- 잘 생각해보면 모든 비즈니스 처리가 반드시 실시간성을 요구하는 것이 아님.
- 어떤 비즈니스는 데이터의 일관성이 실시간으로 맞지 않더라도 어느 일정 시점이 됐을 때 일관성을 만족해도 되는 것이 있음.
- 이러한 개념을 결과적 일관성(eventual consistency)라고 함.
- 결과적 일관성은 고가용성을 극대화함.
- p71 사가 패턴과 이벤트 메시지 기반 비동기 통신 예제 참고
- 이벤트 기반 아키텍처와 메시지 브로커, 사가 패턴으로 비즈니스 정합성을 결과적으로 보장할 수 있고, 비즈니스 및 시스템 가용성을 극대화할 수 있음.
읽기와 쓰기 분리: CQRS 패턴
- 위 그림은 CRUD 기능이 모두 있는 마이크로서비스이다.
- 만약, 서비스의 성능 향상을 위해 서비스 인스턴스를 스케일 아웃해서 여러 개로 실행한 경우, 데이터 읽기/수정 작업으로 인한 리소스 교착상태가 발생할 수 있음.
- 위 문제를 해결하기 위한 방법으로 CQRS 패턴이 있음.
- Command Query Responsibility Segregation, 즉 명령 조회 책임 분리를 의미함.
- 일반적인 비즈니스 모델에서는 입력, 수정, 삭제가 조회보다 적게 쓰이고, 조회 요청이 훨씬 많이 사용됨.
- 서비스 내에 CRUD 모든 기능을 넣어 두면 조회 요청 빈도가 증가함에 따라 다른 명령 기능도 함께 확장해야 하므로 효율적이지 않음.
- 위 그림과 같이 하나의 저장소에 쓰기 모델과 읽기 모델을 분리하는 방식으로 변화시켜서 쓰기, 조회 서비스를 분리할 수 있음.
- 더 나아가 아예 물리적으로 쓰기 트랜잭션용 저장소와 조회용 저장소를 따로 준비할 수 있음.
- 쓰기 전략과 조회 전략을 각각 분리하면, 쓰기 시스템의 부하를 줄이고 조회 대기 시간을 줄이는 엄청난 이점을 누릴 수 있음.
- 위 그림은 CQRS 방식을 이벤트 메시지 주도 아키텍처와 연계한 것임.
- 좌측의 명령 측면은 CUD 처리를 수행하고, 저장소는 쓰기에 최적화된 관계형 데이터베이스를 사용함.
- 프로그래밍 언어도 업무 규칙을 표현하기 좋은 자바 언어를 사용함.
- 오른쪽의 조회 측면은 마이크로서비스에서는 조회 성능이 높은 몽고디비나 엘라스틱서치 같은 NoSQL 데이터베이스를 저장소로 사용함.
- 프로그래밍 언어도 조회를 간단하게 구현할 수 있는 스크립트 기반의 Node.js를 사용함.
- 조회 서비스는 사용량이 많기 때문에 스케일 아웃해서 인스턴스를 증가시켜 놓음.
- 이 구조에서는 명령 서비스가 사용됨에 따라 조회 서비스와의 데이터 일관성이 깨지게 됨.
- 이 데이터 일관성 유지를 위해 필요한 것이 이벤트 주도 아키텍처임.
- 명령 서비스는 저장소에 데이터를 쓰면 저장한 내역이 담긴 이벤트를 발생시켜 메시지 브로커에 전달함.
- 조회 서비스는 메시지 브로커의 이벤트를 구독하고 있다가 이벤트 데이터를 가져와 데이터를 최신 상태로 동기화 함.
- 명령 서비스에 데이터가 들어간 즉시 조회 서비스의 데이터와 일치할 수 없음.
- 하지만 어느 시점이 되면 결과적으로 일치하게 됨 → 결과적 일관성을 추구함.
API 조합과 CQRS
- CQRS는 리소스 교착상태 뿐만 아니라 다른 문제를 해결하기도 함.
- 마이크로서비스의 저장소가 격리돼 있고, 각 마이크로서비스마다 각기 다른 기능을 구현했을 때, 여러 개의 마이크로서비스를 연계해서 서비스로 제공하는 경우, 이를 어떻게 처리해야 하는 가?
- API 조합(composition)
- ex) “주문 이력” 서비스 → 제품, 주문, 고객, 배송 정보가 모두 필요함.
- 따라서, 각 기능을 제공하는 마이크로서비스를 조합하는 상위 마이크로서비스를 만들어 조합된 기능을 제공할 수 있음.
- 하위 서비스는 각자 독립적인 API를 제공하면서 연계 API를 상위 서비스에 정보를 제공함.
- 하지만 이 구조는 상위 서비스가 하위 서비스에 의존하는 결과를 가져옴.
- 이러한 의존성을 줄이기 위한 방법이 필요함.
- CQRS 적용
- 주문 이력 서비스를 제공하는 마이크로서비스가 독자적인 저장소를 갖도록 만듦.
- 또, 주문 이력의 세세한 원천 정보를 보유하고 있는 각각의 서비스도 독자적으로 자신의 저장소를 가지고 서비스를 제공하고 있음.
- 이 원천 정보를 보유한 여러 마이크로서비스는 자신의 서비스의 정보가 변경되는 시점에 변경 내역을 각자의 변경 이벤트로 발행함.
- 주문 이력 마이크로서비스에서는 이 이벤트를 구독하고 있다가, 이벤트를 가져와서 자신의 서비스의 저장소에 기록한다.
- 다른 서비스의 데이터 일관성을 맞추고 서비스의 주문 이력 조회 기능을 제공하게 됨.
댓글