개요
MSA(Micro Service Architecture)를 적용한 프로젝트 내 각 모듈들은 확실히 분리되고 독립성을 가지는 것이 가장 중요합니다.
이 때문에 각 서비스들은 별도 독립된 DB를 가진 상태로 상호 간 API를 이용해 통신하는 케이스가 많습니다.
이런 구조는 모듈끼리 결합도가 강해지는 단점이 있는데 Event-Driven 아키텍처를 적용하면서 이벤트 생산자/소비자 간 결합도를 낮추는 장점을 취할 수 있습니다.
이번 게시글에서는 MSA 개념을 간단히 복습 후 Event-Driven 아키텍처의 장단점 및 해당 아키텍처를 적용한 Redis Streams를 다루겠습니다.
MSA
MSA는 아래의 특성을 가집니다.
- 시스템을 독립적인 단위의 작은 서비스들로 분리
- 각 서비스들은 별도 DB를 가지고 다른 서비스의 DB 접근 불가
- 각 서비스들은 API를 통해서만 통신
MSA를 이상적으로 도입했을 때 장단점은 아래와 같습니다. (이상과 현실은 많이 다르다는 것을 개발 연차가 쌓이면서 깨닫고 있습니다..)
장점
- 높은 응집도, 낮은 결합도
- 서비스 별로 독립적인 개발과 배포 가능
- 서비스 크기가 작아져 유지보수가 용이함
- 서비스 간 결합도가 낮을 경우보다 빠른 개발, 테스트, 배포가 가능
- 서비스 별로 개별 확장 가능
- 적절한 fallback 처리를 했을 경우 일부 서비스 실패가 전체 시스템 실패로 이루어지지 않음
단점
- 분산 시스템의 문제점을 고스란히 가져감 (부분 장애, 네트워크 실패, 데이터 동기화, 로드 밸런싱, 개발 및 관리의 복잡성)
- 서비스들을 분리할수록 통합 테스트가 어려움
- 서비스들이 분리되어있기 때문에 모니터링/디버깅 복잡도 증가
- 트랜잭션 관리의 어려움
- 서비스 간 통신 구조에 대한 고민 필요 (동기 vs 비동기, HTTP vs GRPC, 통신 브로커, etc.)
더 자세한 내용은 제가 예전에 작성한 게시글을 참고 바랍니다.
https://jaimemin.tistory.com/1827
Event-Driven 아키텍처
Event-Driven 아키텍처는 이벤트의 consumer/producer 구조로 통신이 이루어지며 분산 시스템에서의 통신 방식 중 하나입니다.
MSA로 분리된 각 서비스들은 이벤트 저장소인 Event-broker와의 의존성만 가집니다.
Event-Driven 아키텍처를 도입했을 때 장단점은 아래와 같습니다.
장점
- 위 그림을 보면 알 수 있다시피 공통적인 Event-broker에 대한 결합만 있기 때문에 이벤트 producer/consumer 간의 결합도가 낮아짐
- 결합도가 낮아지기 때문에 서버 추가, 삭제 시 타 서버를 변경할 필요가 적어져 유연한 변경 가능
- 장애가 발생하더라도 이벤트는 저장되고 이후에 해소되었을 때 처리가 되기 때문에 장애 탄력성을 갖춤
단점
- 모듈 간 느슨하게 연결되었기 때문에 시스템의 예측 가능성 떨어짐
- 통합 테스트 어려움
- 장애 추적 어려움
Redis Streams
Redis Streams는 append-only log를 구현한 자료구조로 하나의 key로 식별되는 하나의 stream에 엔트리가 계속 추가되는 구조입니다.
하나의 엔트리는 entry ID + (key-value 리스트)로 구성이 되며 append-only이기 때문에 추가된 데이터는 사용자가 삭제하지 않는 한 지워지지 않습니다.
- 하나의 entry는 로그 한 줄
Redis Streams 활용 사례는 아래와 같으며 이 중 주로 Event-driven 아키텍처 내 이벤트 저장소로 사용이 됩니다.
- 센서 모니터링 (지속적으로 데이터 수집)
- 유저별 알림 데이터 저장
- 이벤트 저장소
Redis Streams 명령어
XADD
- 특정 key의 stream에 엔트리를 추가하는 명령어
- 해당 key 내 stream이 없으면 생성
ex) user-notifications라는 stream에 1개의 엔트리를 추가하여 3개의 field-value 쌍을 넣는 케이스
XADD user-notifications * message1 hi message2 hello message3 greetings
XRANGE
- 특정 ID 범위의 엔트리를 반환
ex) user-notifications의 모든 범위 조회
XRANGE user-notifications - +
XREAD
- 한 개 이상의 key에 대해 특정 ID 이후의 엔트리 반환
- 명령어 내 BLOCK 포함 시 동기, 미포함 시 비동기 방식으로 진행
ex) user-notifications의 0보다 큰 ID 조회 (동기)
XREAD BLOCK 0 STREAMS user-notifications 0
ex) user-notifications의 0보다 큰 ID 조회 (비동기)
XREAD 0 STREAMS user-notifications 0
ex) user-notifications에서 새로 들어오는 엔트리를 동기 방식으로 조회 (event listener와 같은 방식)
XREAD BLOCK 0 STEAMS user-notifications $
XGROUP CREATE
- 한 stream을 여러 consumer가 분산 처리할 수 있으며 하나의 그룹에 속한 consumer는 서로 다른 엔트리를 조회
- XGROUP CREATE는 consumer group을 생성하는 명령어
ex) user-notifications에 consumer-group1이라는 consumer-group 생성
XGROUP CREATE user-notifications consumer-group1
XREADGROUP
- 특정 key의 stream을 조회하는데 특정 consumer group에 속한 consumer로 읽음
ex) user-notifications에서 consumer-group1 그룹으로 2개의 consumer가 각각 1개씩 조회
- 끝에 >를 붙임으로써 아직 소비되지 않은 메시지를 가져옴
XREADGROUP GROUP consumer-group1 consumer1 COUNT 1 STREAMS user-notifications >
XREADGROUP GROUP consumer-group1 consumer2 COUNT 1 STREAMS user-notifications >
Redis Streams 활용 예시
시스템이 주문, 결제, 알림 서비스로 분리되어있고 event-driven 아키텍처를 적용하여 각각의 모듈은 redis streams 이벤트 브로커에 대한 결합만 있다고 가정합니다.
전체적인 프로세스는 아래와 같이 동작할 것입니다.
- 클라이언트가 주문을 하여 주문 서비스에서 주문 이벤트(order-events) 생성
- 결제 서비스 내 이벤트 리스너가 구현되어 있어 주문 이벤트가 발생할 때마다 읽어오고 결제 처리 및 결제 완료(payment-events) 이벤트 생성
- 알림 서비스는 주문 이벤트가 발생할 때마다 주문 상태 알림을 클라이언트에게 발송
- 알림 서비스는 결제 완료 이벤트가 발생할 때마다 결제 완료 알림을 클라이언트에게 발송
참고
백엔드 개발자를 위한 한 번에 끝내는 대용량 데이터 & 트래픽 처리 초격자 패키지 Online - 고성능 서비스를 위한 Redis 활용 및 아키텍처
'Redis' 카테고리의 다른 글
[Redis] Redis 성능 튜닝 (1) | 2023.11.21 |
---|---|
[Redis] Redis 클러스터 개념 정리 (0) | 2023.11.20 |
[Redis] Redis 백업 및 장애 복구 방법 정리 (0) | 2023.11.15 |