대규모 서비스를 위한 백엔드 확장 전략 (2): MSA와 데이터 샤딩
원티드 프리온보딩 백엔드 챌린지 아키텍쳐 설계을 정리한 내용입니다.
1.메시지 큐
1.1 메시지 큐란
생성자는 이벤트를 발행하여 메시지 큐에 보관하고, 메시지큐는 소비자가 꺼낼 때까지 안전하게 저장한다. 비동기 통신 매체이다.
1.2 장점
서비스 또는 서버 간 결합을 느슨하게 해주기 때문에 규모 확장성이 보장되어야 하는 어플리케이션을 안전하게 구성할 수 있다.
느슨한 결합은 소비자가 죽어도 생산자가 메시지를 발행할 수 있고, 반대로 생산자가 죽어도 소비자는 메시지 큐에 발행된 메시지를 구독할 수 있다.
알림 푸쉬 발송, 이메일 발송, 이미지 보정 등 시간이 오래 걸릴 수 있는 프로세스를 비동기적으로 처리할 때 사용한다.
1.3 이벤트 큐
메시지 큐와 다르게 한 번 읽은 데이터가 즉시 삭제 되지 않는다. MSA를 구현하는데 많이 사용되는데 아래와 같은 특징이 있다.
- 단일 진실 공급원
- 장애가 일어난 시점부터 재처리가 가능
- 많은 양의 실시간 데이터를 효율적으로 처리
2. MSA
Event Driven MSA에서는 시스템 이벤트를 생성/소비 하는 방식으로 서로 통신한다. 이벤트를 소비하여도 전달한 시스템에서 바로 사라지지 않고 다른 컨슈머도 가져갈 수 있도록 보존된다.
2.2 DDD와 Bounded Content
Bounded Content를 비지니스 중심으로 설계하면 팀별로 느슨하게 결합되기 때문에 고도로 응집된 MSA를 할 수 있다.
2.3 이벤트 기반 통신 구조
요청/응답 API에 의존하지 않고 이벤트 스트림 내부에 정의된 이벤트 데이터로 소통한다. 즉, 생산자는 자신이 맡은 이벤트 스트림에 데이터를 잘 생산하는 것에 집중하고 소비자는 이벤트 스트림에서 들어온 데이터를 잘 처리하는 일만 담당한다.
2.4 동기식 마이크로서비스의 문제점
- 점대점 결합 : 다른 서비스 의존한다.
- 의존적 확장 : 의존하는 다른 모든 서비스가 확장이 가능한지 고려한다.
- API 버저닝 : API 스펙을 변경하기 어렵다
- 테스트 : 의존하는 서비스까지 통합해서 테스트하기 어렵다
2.5 비지니스 토폴로지
비지니스 토폴로지는 마이크로 서비스 + 이벤트 스트림 + API의 집합
이다. 마이크로서비스는 비지니스 Bounded Content를 구현하고, 이벤트 스트림은 전체 Context의 도메인 데이터를 공유할 때 필요한 데이터 통신 수단으로 활용한다.
3. 마이크로서비스 커뮤니케이션 패턴들
3.1 Synchronous Calls
구현하기 가장 쉬운 패턴이다. 서비스1은 서비스2가 요청 처리를 완료하고 응답을 반환할 때까지 대기한다.
문제점
은 서비스1이 요청을 보내고 서비스2가 요청을 처리하는 도중 서비스 1에서 타임아웃이 발생한다면 두 서비스 간 데이터가 일치 하지 않는 문제가 있을 수 있다. 또한 서비스 간 강한 결합이 생성된다.
3.2 Simple Messaging
메시지 브로커를 기준으로 페이로드가 포함된 메시지를 서로 주고 받기만 하면되는 패턴이다. 메시지 브로커 라이브러리에 따라 Automatic Retry
기능이 제공된다.
서비스2는 서비스1을 호출하지 않기 때문에 느슨한 결합을 유지할 수 있다. 하지만 메시지 브로커에 문제가 발생하면 안되고, 메시지 구조가 변경되는 경우 모든 클라이언트가 변경된 메시지 구조를 처리할 수 있어야 한다.
3.3 Transactional Messaging
메시지를 브로커에 전달하기 전에 데이터베이스에 저장하는 방식이다.
보내는쪽과 받는 쪽의 로컬 DB에 메시지가 저장되기 때문에 언제든지 롤백과 복구가 가능하다. 즉, 메시지 브로커에 장애가 나더라도 괜찮다는 장점이 있다.
하지만 아키텍처를 구성하기 복잡하다. 또한 데이터베이스로부터 데이터를 폴링하고 처리하지 않은 메시지에 대한 처리 로직을 작성해야한다.
3.4 Zero-Payload Event
페이로드에 대한 포인터만 메시지로 전달한다. 예로 OrderId = 4711 주문이 배송되었다는 메시지를 발행한다면, 서비스1의 메시지에 EventType = orderShipped, orderId = 4711
만 포함해서 브로커에 전달한다. 수신자는 송신자를 호출해서 주문 데이터를 요청한다.
메시지 구조가 심플해서 하위 호환성에 대한 고려가 필요없다. 또한, 메시지 브로커에 장애가 발행해도 재시도를 할 수 있따. 페이로드는 서비스1를 직접해서 얻기 때문이다. 하지만 이벤트 페이로드를 얻기 위해서 서비스간 동기적 호출이 필요하고, 서비스의 복잡도가 올라간다.
4. 샤딩
데이터베이스를 수평적으로 확장(Scale Out)하는 것을 의미한다.
샤딩은 대규모 데이터베이스를 샤드라고 부르는 작은 단위로 분할하는 것을 의미한다. 모든 샤드는 같은 스키마를 쓰지만 샤드에 보관되는 데이터 사이에는 중복이 없다.
단점은 여러 사드에 데이터가 나누어져 있기 때문에 JOIN
이 어렵고, 핫스팟 키 문제로 특정 샤드에 쿼리가 집중 될 수 있다.
5. 처리율 제한
클라이언트 또는 서비스가 보내는 트래픽의 처리율을 제어하기 위한 장치이다. 특정 임계치(Threshold
)를 넘으면 그 이후의 요청은 중단시킨다.
MSA인 경우, 처리율 제한 장치는 보통 API Gateway
에 구현한다. API Gateway에서는 처리율 제한, SSL 종단, 사용자 인증, IP 허용 목록 관리등을 할 수 있다.
알고리즘
토큰 버킷 알고리즘
토큰이 주기적으로 채워지고, 각 토큰이 처리될 떄마다 하나의 토큰을 사용한다. 만약 토큰이 없다면 해당 요청은 버려진다.
AWS에서 사용하고 있는 알고리즘으로 통상적으로 API 엔드포인트마다 별도의 버킷을 뒨다. IP 주소별로 처리율 제한을 적용해야한다면, IP 주소마다 버킷을 하나씩 할당해야한다.
장점
은 구현이 쉽고, 짧은 시간에 집중되는 트래픽도 잘 처리하며, 메모리 사용도 효율적이다.
단점
은 버킷의 크기, 토큰 공급률이라는 두 개의 인자가 필요한 알고리즘이라 적절히 튜닝하는게 어렵다.