1) 메세지 큐
메세지 큐에 일단 보관한 메시지는 소비자가 꺼낼 때까지 안전하게 보관할 수 있다
장점 : 서비스 또는 서버 간 결합이 느슨해져, 규모 확장성이 보장되어 안정적인 애플리케이션을 구성할 수 있다. 생산자는 소비자의 프로세스가 다운되어도 메시지를 발행할 수 있고, 그 반대도 가능하다
사용 사례
알림 푸시 발송, 이메일 발송, 이미지 보정 등 시간이 오래 걸릴 수 있는 프로세스를 비동기로 처리한다
이벤트 큐 : 메시지 큐와 다르게 한 번 읽은 데이터는 즉시 삭제되지 않는다
오늘날의 애플리케이션은 이벤트 기반 MSA를 구현할 뗴 이벤트 큐를 많이 활용한다
특징 : 단일 진실 공급원, 장애가 일어난 시점부터 재처리 가능, 많은 양의 실시간 데이터를 효율적으로 처리 가능
https://www.youtube.com/watch?v=H_DaPyUOeTo
https://tv.kakao.com/channel/3150758/cliplink/391419257
2) 이벤트 기반 MSA
현대 EDMSA(Event Driven MSA)에서는 시스템 이벤트를 생성/소비하는 방식으로 서로 통신한다
이벤트를 소비해도 전달한 시스템에서 바로 사라지지 않고 다른 컨슈머도 가져갈 수 있게 보존된다
DDD와 Bounded context
- Bounded context를 비즈니스 중심으로 설계하면 팀 별로 느슨하게 결합하며 고도로 응집된 MSA를 할 수 있어 비즈니스에서 필요한 솔루션을 자율적으로 설계 및 구현 ~ POlYGLOT 가능
- 팀 간 복잡한 의존 관계가 줄어든다
- 기술 중심으로 설계할 경우 모놀리식 시스템과 같은 문제를 겪는다
이벤트 기반 통신 구조
- 요청과 응답 API에 얽매이는 일 없이 이벤트 스트림 내부에 정의된 이벤트 데이터를 매개로 소통한다. 생산자는 자신이 맡은 이벤트 스트림에 잘 정의된 데이터를 생성하는 일만 책임지면 된다. 소비자는 이벤트 스트림에서 들어온 데이터를 처리하는 일을 담당한다. 느슨한 결합과 높은 응집도를 추구하는 Bounded context 원칙을 더 확실히 준수할 수 있다
동기식 마이크로서비스의 문제
https://www.youtube.com/watch?v=J-VP0WFEQsY
1. 점대점 결합 : 다른 서비스에 의존한다
2. 의존적 확장 : 의존하는 다른 모든 서비스가 확장이 가능한지 고려해야 한다
3. API 버저닝 : API 스펙을 변경하는 건 매우 어렵다
4. 테스트 : 의존하는 서비스까지 통합해서 테스트하기 어렵다
비즈니스 토플로지
마이크로서비스 + 이벤트 스트림 + API 집합이다
마이크로서비스는 비즈니스 Bounded context를 구현한다
이벤트 스트림은 전체 Context의 도메인 데이터를 공유하기 위해 필요한 데이터 통신 수단이다
3) Microservice Communication Patterns
1. Synchronous Calls
- 구현하기 쉬운 Communication Pattern이다
- 서비스1은 서비스2가 요청 처리를 완료하고 응답을 반환할 때까지 대기한다
- 동기식이기 때문에 구조가 간단하다
- Netflix의 Feign 그리고 Hystrix(Circuit Breaker)를 사용할 수 있다
Timeouts
- 서비스1이 서비스2로 요청을 보내고 서비스2가 요청을 처리 도중에 서비스1에서 timeout 예외가 발생한다면
두 서비스 간 데이터가 불일치할 수 있다
Strong Coupling
동기 서비스 간 강한 결합 생성
Easy to Implement
동기 서비스는 구현이 쉽다
2. Simple Messaging
- 서비스1은 메시지 브로커에게 메시지만 보내고 잊으면 된다
- 서비스2는 메시지 브로커에게 메시지를 구독하기만 하면 된다
- 서비스1과 2는 서로의 존재를 알 필요가 없다
- 메시지 브로커를 기준으로 페이로드가 포함된 메시지를 서로 주고 받기만 하면 된다
Automatic Retry
메시지 브로커 라이브러리에 따라 Retry 기능 제공
서비스2가 사용이 불가능한 상태일 때, 서비스2가 정상화 될 때 까지 메시지 전달을 시도
물론 서비스2가 polling 하는 구조라면 고려할 필요는 없다
Loose Coupling
서비스2는 서비스1 을 호출하지 않기 때문에 느슨한 결합을 유지할 수 있다
Message Broker must not fail
메시지 브로커에 문제가 발생하면 안된다
메시지 브로커에 데이터가 중앙 집권화되기 때문에 hell will break loose 할 것이다
Pipeline contains Schema
메시지 구조가 변경된 경우 모든 클라이언트가 변경된 메시지 구조를 처리할 수 있어야 한다. 이는 마이크로서비스의 주요 목표 중 하나인 independent deployments (독립 배포)와는 모순된다. 이러한 모순은 하위 호환성이 보장되게 변경하는 것으로 완화할 수 있다
Two-Phase Commit
서비스1, 서비스2 를 서로 같은 트랜잭션으로 묶고 싶다면 Two-Phase commit 을 사용할 수 있다. 단, 데이터베이스나 메시지 브로커가 지원을 안할 수도 있으며 좋은 성능을 기대하기 힘들다 (거의 사용하지 않음)
+ https://youtu.be/urpF7jwVNWs?t=420
만약에
→ A 라는 서비스가 있다.
→ 이 서비스의 특정 API 는 Product 테이블과 Review 테이블을 Join 해서 보여주고 있다.
→ 근데 갑자기 MSA 하면서 Product 테이블을 직접 볼 수 없게 되고 Review 볼 수 없게 되었다.
→ Product API, Review API 각각 찔러서 애플리케이션에서 데이터를 조합해서 화면에 보여준다.
→ 단점: 일단 API 의존도가 생기고, 페이징처리 어렵다.그룹바이 쿼리 이런거 힘듬 할 수 있는게 많지 않음
→ 계속 API 찌리니까 트래픽도 많이 발생시킴
→ 그럴바엔 Product, Review 쪽에서 이벤트 발행하고 A 라는 서비스는 내부DB 에 이 데이터들을 저장한다.
→ 그리고 API 호출이 들어오면 내부 DB 에서 조인해서 보여준다.
3. Transactional Messaging
특징
- 메세지를 브로커에 전달하기 전에 데이터베이스에 저장하는 방식
- 수신자는 메시지를 송신하고 처리하기 전에 데이터베이스에 저장한다
No Need for Two-Phase Commit
- 메시지를 보내는 쪽과 받는 쪽의 로컬 데이터베이스에 저장하기 때문에 언제든 롤백, 복구가 가능하다
Message Broker may Fail
- 데이터베이스에 메시지가 저장이 되기 때문에, 브로커에 장애가 나더라도 데이터베이스에서 메세지를 조회하면 된다
Complex Setup
- 아키텍처를 구성하기엔 다소 복잡하다. 발행해야 하는 메시지를 데이터베이스에 저장해야 되기 때문이다. 또한 데이터베이스로부터 데이터를 pulling하고 처리하지 않은 메시지에 대한 처리 로직을 작성해야한다
-> 송신측 : 메세지 브로커로 전송/ 수신측 : 메시지를 처리하는 비즈니스 로직 호출
4. Zero-Payload Events
특징
페이로드에 대한 포인터만 메시지로 전달한다. 예를 들어 Order Id = 4711 주문이 배송되었다는 메시지를 발행할 경우, 제로페이로드 방식을 사용한다면 서비스1은 메시지에 Event Type = orderShipped 그리고 Order Id = 4711만 포함해서 브로커에 전달한다. 그리고 수신자는 송신자를 호출해 주문 데이터를 요청한다
Dump pipe
- 메시지 구조가 정말 심플하기 때문에 하위 호환성에 대해 고려할 필요가 없다
Combinable with Transactional Messaging
- 메시지 브로커에 장애가 발생해도 재시도를 할 수 있다.(페이로드는 서비스1을 호출해서 얻기 때문)
- 이벤트 페이로드를 얻기 위해 서비스 간 동기적 호출이 필요하고, 서비스의 복잡도가 올라간다
4) 샤딩
- 데이터베이스를 수평적 확장하는 것을 의미한다(Scale-out). 더 많은 서버를 추가함으로 성능을 향상시킬 수 있다
- 대규모 데이터베이스를 샤드라고 부르는 작은 단위로 분할하는 것을 의미한다. 모든 샤드는 같은 스키마를 쓰지만 샤드에 보관되는 데이터 사이에는 중복이 없다
단점
- 여러 샤드에 걸친 데이터를 조인하기 매우 어렵다
- 핫스팟 키 문제 : 특정 샤드에 쿼리가 집중이 될 수 있다
- 데이터 재 샤딩의 어려움
5) 처리율 제한
1. 처리율 제한
- 클라이언트 또는 서비스가 보내는 트래픽의 처리율을 제어하기 위한 장치. 특정 임계치를 넘으면 그 이후 요청은 중단시킨다
2. 사용 사례
- 사용자는 초당 2회 이상 새 글을 올릴 수 없다. 같은 IP 주소로는 하루에 10개 이상 계정을 생성할 수 없다. 같은 디바이스로는 주당 5회 이상 리워드을 요청할 수 없다
3. 설계
(1) 클라이언트 측
- 위변조가 가능하며 권장하지 않는다
(2) 서버 측
(3) 미들웨어
- MSA인 경우, 처리율 제한 장치는 보통 API Gateway에 구현한다
- API Gateway : 처리율 제한, SSL 종단, 사용자 인증, IP 허용 목록, 관리 등
(4) 정리
- 현재 기술 스택이 서버 측에 기능 구현이 가능한지 점검
- 상황에 맞는 알고리즘 사용, 만약 제 3 사업자가 제공하는 API Gateway를 사용한다면 선택지는 제한이 될 수 있다
- MSA에 기반하고 있다면 인증, IP 허용 같은 기능을 이미 API Gateway에 적용했을 수 있다. 그러면 처리율 제한도 API Gateway에 포함하는 것이 좋다
- 충분한 인력이 없다면 상용 솔루션도 고려해보는 것이 좋다
4. 알고리즘
(1) 토큰 버킷 알고리즘
- 토큰이 주기적으로 채워진다
- 각 요청이 처리될 때마다 하나의 토큰을 사용한다
- 토큰이 없다면 해당 요청은 버려진다
(2) 특징
- 많은 기업이 보편적으로 사용하는 알고리즘
- 통상적으로 API 엔드포인트마다 별도의 버킷을 둔다
- IP 주소별로 처리율 제한을 적용해야 한다면 IP 주소마다 버킷을 하나씩 할당해야 한다
- 시스템의 처리율을 초당 10,000개 요청으로 제한한다면, 모든 요청이 하나의 버킷을 공유하도록 해야 한다
(3) 장단점
장점
- 구현이 쉽다
- 메모리 효율적
- 짧은 시간에 집중되는 트래픽도 잘 처리
단점
- 버킷 크기 & 토큰 공급률 두 개의 인자를 필요로 하는 알고리즘이기 때문에 적절하게 튜닝하는 것이 어렵다
케이스 : 500 에러가 1분에 10건이 발생하면 슬랙으로 알림을 쏜다
- 500 에러 발생 시, 레디스에 카운트 한다
- 1분마다 카운트 리셋
- 스케줄러로 30초마다 체크하고 500 에러가 10건이 넘어가면 슬랙 알림을 쏜다
'개발공부 > 원티드 챌린지 정리' 카테고리의 다른 글
8월 백엔드 챌린지 : 도커 프로 (2) (0) | 2023.10.02 |
---|---|
8월 백엔드 챌린지 : 도커 프로 (1) (0) | 2023.09.21 |
7월 백엔드 챌린지 4. 예제 적용 (0) | 2023.09.18 |
7월 백엔드 챌린지 2. 사용자 수에 따른 규모를 확장하는 방법 (0) | 2023.08.01 |
7월 백엔드 챌린지 1. 기술 면접에서 시스템 설계 문제가 가지는 의미 (0) | 2023.07.31 |