[MSA] 07. 마이크로서비스 쿼리 구현

Jimin Lim·2024년 1월 4일
0

Architecture

목록 보기
17/23

MSA에서는 다음 두 가지 패턴으로 쿼리를 구현한다.

  • API 조합 패턴: 클라이언트가 데이터를 가진 여러 서비스를 직접 호출해 그 결과를 조합하는 패턴
  • CQRS 패턴 (커맨드 쿼리 책임 분리): 쿼리만 지원하는 하나 이상의 뷰 전용 DB를 유지하는 패턴

7.1 API 조합 패턴 응용 쿼리

API 조합 패턴 개요

  • API 조합기: 프로바이더 서비스를 쿼리해 데이터 조회
  • 프로바이더 서비스: 최종 결과로 반환할 데이터의 일부를 갖고 있는 서비스

애그리거트가 거대한 데이터 뭉치를 비효율적으로 인메모리 조인을 해야 할 수도 있다!

API 조합 설계 이슈

어느 컴포넌트를 쿼리 작업의 API 조합기로 선정할 것인가?

  1. 서비스 클라이언트 내에 위치
  2. 애플리케이션의 외부 API가 구현된 API 게이트웨이
  3. 스탠드 얼론 서비스

어떻게 해야 효율적으로 취합 로직을 작성할 것인가?
리액티브 프로그래밍 모델 사용해야 한다. 순서가 필요하면 일부 순차 호출

API 조합 패턴의 장단점

  • 오버헤드 증가: 여러 번 요청하고 여러 DB 쿼리 실행해야하니까
  • 가용성 저하 우려: 하나의 서비스로 처리하는 것에 비해 가용성 낮아진다.
    • 데이터 캐싱, 미완성 데이터 반환 방식으로 가용성을 높일 수 있다.
  • 데이터 일관성 결여

7.2 CQRS 패턴

  • 트랜잭션: RDBMS
  • 텍스트 검색 쿼리: Elastic Search 등 텍스트 검색 DB

이런 종류의 아키텍처를 일반화 함

CQRS의 필요성

구현하기 어려운 다중 서비스 쿼리 예시
Filter 할 때 매치할 keywords 속성이 있는데 해당 속성이 모든 서비스에 존재 하지 않는 경우

-> API 조합기로 인메모리 조인하거나 / 먼저 조회하고 다른 서비스에 조회하고.. 어쩌구
-> 거대한 데이터를 가져옴 / 과도한 네트워크 트래픽 유발

어려운 단일 서비스 쿼리 예시

  • 데이터를 가진 서비스에 쿼리를 구현하는 게 부적절한 경우: 음식점 정보 제공하는 서비스에서 주어진 위치로 배달 가능한 음식점 검색하도록 하면...
  • 서비스 DB가 효율적인 쿼리를 지원하지 않는 경우: DB에서 지리 공간 기능을 지원하지 않는 경우

CQRS의 개요

결론은 ..

  1. 비효율적인 인 메모리 조인 필요
  2. 서비스 DB가 효율적인 쿼리를 지원하지 않는 경우
  3. 관심사 분리 필요

이러한 문제가 있는데, 이걸 해결할 수 있는 묘안이 CQRS 패턴이다.

CQRS는 커맨드, 쿼리 분리

  • 커맨드: CUD
  • 쿼리: R, 지원해야 하는 쿼리에 대해 모든 종류의 DB 지원/ 도메인 이벤트 구독 후 DB 업데이트

CQRS의 장점

  1. MSA에서 쿼리를 효율적으로 구현
  2. 다양한 쿼리를 효율적으로 구현: 각 쿼리가 효율적으로 구현된 하나 이상의 뷰를 정의해 단일 데이터 저장소 한계 극복
  3. 이벤트 소싱 애플리케이션에서 쿼리 가능: 기본키 쿼리만 지원하는 한계 극복
  4. 관심사 더 분리

CQRS의 단점

  1. 아키텍처 복잡
  2. 복제 시차 처리 -> 모바일에서는 로컬 모델 업데이트해서 시차 해소한다..!

7.3 CQRS 뷰 설계

  • 이벤트 핸들러: 이벤트 구독해서 DB 업데이트
  • 쿼리 API: 데이터 조회

뷰 DB 선택

1) SQL vs NoSQL

  • RDBMS
  • NoSQL DB: 트랜잭션 기능 제한적이지만 유연성/확장성에 유리 -> CQRS 뷰와 잘 맞는 편

2) 업데이트 작업 지원
이벤트 핸들러에서 기본키로 수정/삭제 할테지만 외래키를 사용하는 경우도 존재한다.

  • RDBMS, MongoDB 는 필요한 칼럼 인덱스 생성하면 됨
  • 다른 NoSQL DB는 비기본키 기반으로 업데이트하기가 쉽지 않다.
    • DynamoDB는 기본키 기반의 수정/삭제만 지원되기에 보조 인덱스를 쿼리해서 수정/삭제할 항목의 기본키 결정해야한다.

데이터 접근 모듈 설계

동시성 처리
뷰가 여러 종류의 애그리거트가 발행한 이벤트를 구독한다면 동일한 레코드에 대해 동시 업데이트할 수 있다.

멱등한 이벤트 핸들러
같은 이벤트를 여러 번 받을 수 있다. 이때 처리 후 결과가 완전히 동일하면 문제없지만 처리할 때마다 달라진다면 문제가 발생한다.

  • 이벤트 ID 로 중복확인
  • [애그리거트타입, 애그리거트ID] -> max(eventId) 이렇게 담고 있으면 됨

클라이언트 애플리케이션이 최종 일관된 뷰를 사용할 수 있다
1. 커맨드 작업 후 이벤트 ID가 포함된 토큰 반환
2. 이 토큰을 쿼리 작업에 전달

해당 이벤트에 의해 뷰가 업데이트되지 않는다면 에러 반환 -> 중복 이벤트 감지 메커니즘을 뷰 모듈에 구현 가능

CQRS 뷰 추가 및 업데이트

아카이빙된 이벤트 이용해 CQRS 뷰 구축
메시지 브로커가 메시지를 무기한 보관할 수 없으므로 AWS S3처럼 아카이빙 필요

CQRS 뷰를 단계적으로 구축

  1. 주기적으로 각 애그리거트 인스턴스의 스냅샷을 그 이전의 스냅샷과 이 스냅샷이 생성된 이후 쭉 발생한 이벤트 바탕으로 계산
  2. 계산된 스냅샷과 그 이후 발생한 이벤트 이용해서 뷰 생성

7.4 CQRS 뷰 구현: AWS DynamoDB 응용

DynamoDB: NoSQL, 이름-값 쌍

EventHandler

  • 이벤트 소비해서 테이블 업데이트 한다..

DynamoDB 데이터 모델링 및 쿼리 설계

  • 테이블 설계: 기본키로 삽입/수정/조회, items 처럼 리스트로 저장되는 건 맵 리스트 형태로 저장됨
  • 쿼리 전용 인덱스
    • 파티션 키: Z축 확장할 때 이 키를 보고 저장소 파티션 선택
    • 정렬 키: 해당 키로 정렬
  • 중복 이벤트 감지: UpdateItem()(개별 아이템 업뎃, 필요시 생성) 메커니즘 이용해 중복 이벤트가 아닐 때만 아이템 업데이트
    • <애그리거트타입애그리거트ID>:이벤트ID 저장한 후 새로 들어온 이벤트 ID가 작거나 같으면 중복이라고 판단

DataAccess

  • DynamoDB 테이블 및 관련 헬퍼 클래스를 조회/수정하는 메서드가 정의된 DAO 포함
profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글