Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

마이크로서비스 인프라에서 Redis 활용법: 메시지 큐와 이벤트 스토어 구축하기

2019년에 저는 Redis로 이벤트 스토어를 구축하는 방법에 대한 글을 작성한 바 있습니다. 당시 Redis Streams가 트랜잭션 로그처럼 불변의 append-only 방식으로 이벤트를 저장할 수 있어 이벤트 스토어에 매우 적합하다는 점을 설명했습니다. 이번 글에서는 해당 블로그에서 소개했던 OrderShop 샘플 애플리케이션의 업데이트 버전을 통해 Redis를 메시지 큐로 활용하는 방법을 시연하며, 캐싱을 넘어선 Redis Enterprise의 다양한 활용 사례를 소개하고자 합니다.

마이크로서비스, 인프라 서비스, 분산 시스템 한눈에 살펴보기

Redis는 메시지 큐나 이벤트 스토어 같은 인프라 서비스를 구축하기에 훌륭한 솔루션입니다. 다만 마이크로서비스 아키텍처로 분산 시스템을 구축할 때는 몇 가지 고려해야 할 사항이 있습니다. 관계형 데이터베이스는 모놀리식 애플리케이션에 적합했지만, 마이크로서비스 아키텍처에 요구되는 확장성과 가용성은 Redis와 같은 NoSQL 데이터베이스만이 충족시킬 수 있습니다.

분산 시스템은 곧 분산된 상태(distributed state)를 의미합니다. CAP 정리에 따르면 소프트웨어 구현체는 일관성(Consistency), 가용성(Availability), 파티션 허용성(Partition tolerance)이라는 세 가지 속성 중 두 가지만 제공할 수 있습니다. 따라서 장애 허용(fault tolerant) 시스템을 만들려면 가용성과 일관성 중 하나를 선택해야 합니다. 가용성을 선택하면 최종적 일관성(eventual consistency)을 갖게 되는데, 이는 데이터가 일정 시간이 경과한 후에야 일관성을 갖추게 된다는 의미입니다. 반대로 일관성을 선택하면 분산 시스템 전반에서 쓰기 작업을 동기화하고 격리해야 하므로 성능 저하가 발생합니다.

이벤트 소싱(Event Sourcing)은 주문이나 고객 같은 비즈니스 엔티티의 상태를 상태 변경 이벤트의 연속으로 저장하는 방식으로, 일관성 대신 가용성을 우선합니다. 덕분에 쓰기 작업은 매우 단순해지지만, 여러 서비스에 걸치는 읽기 작업은 읽기 모델(read model)과 같은 추가 메커니즘이 필요할 수 있어 상대적으로 비용이 높아집니다.

분산 시스템에서의 통신은 브로커 기반(brokered) 또는 브로커리스(brokerless) 방식으로 이루어질 수 있습니다. 브로커리스 방식은 이미 널리 알려져 있으며, HTTP가 가장 대표적인 예입니다. 브로커 기반 방식은 이름 그대로 메시지 발신자와 수신자 사이에 브로커가 존재합니다. 이는 발신자와 수신자를 디커플링하여 동기식·비동기식 통신을 모두 지원하게 해줍니다. 메시지가 전송되는 시점에 소비자가 반드시 가용 상태일 필요가 없으므로 시스템 전체가 더 탄력적으로 동작하며, 발신자와 수신자를 독립적으로 확장(scaling)할 수 있다는 장점도 있습니다.

(자세한 내용은 동기식·비동기식 통신 요구 사항에 따른 선택 가이드—Redis Streams, Redis Pub/Sub, Kafka 등—에 관한 포스트를 참고하세요.)

OrderShop: 전자상거래 샘플 구현

마이크로서비스 아키텍처의 'Hello World'라 불리는 OrderShop은 이벤트 기반 접근 방식을 적용한 간단한 전자상거래 시스템 구현입니다. 이 샘플 애플리케이션은 단순한 도메인 모델을 사용하지만, 애플리케이션의 목적을 충분히 수행합니다.

OrderShop은 Docker Compose로 오케스트레이션되며, 모든 네트워크 통신은 gRPC를 통해 이루어집니다. 핵심 구성 요소는 이벤트 스토어와 메시지 큐이며, 모든 서비스는 gRPC를 통해 오직 이들과만 연결됩니다. OrderShop은 Python으로 작성된 샘플 구현으로, GitHub에서 소스 코드를 확인할 수 있습니다.

(참고: 이 코드는 프로덕션 환경에서 사용할 준비가 되어 있지 않으며, 데모 목적으로만 제공됩니다!)

실행 방법

  • GitHub 저장소 클론: https://github.com/redis-demos/ordershop-v2
  • 다음의 간단한 5단계로 OrderShop v2를 실행할 수 있습니다:
  1. docker-compose up 명령으로 애플리케이션 시작
  2. 브라우저에서 https://localhost:5000/ 접속
    1. 이벤트를 실시간으로 확인하고 상태를 탐색
  3. python -m unittest tests/unit.py 명령으로 클라이언트 실행
  4. 브라우저에서 새 탭을 열어 https://localhost:8001/ 접속
    1. redis:6379로 테스트 데이터베이스에 연결
  5. docker-compose down 명령으로 애플리케이션 종료

OrderShop v2 아키텍처

이 샘플의 서버 아키텍처는 여러 서비스로 구성됩니다. 상태는 여러 도메인 서비스에 분산되어 있지만, 단일 이벤트 스토어에 저장됩니다. Read model 컴포넌트는 아래 그림과 같이 상태를 읽고 캐싱하는 로직을 전담합니다:

마이크로서비스 인프라에서 Redis 활용법: 메시지 큐와 이벤트 스토어 구축하기

명령(Command)과 질의(Query)는 Message queue 컴포넌트를 통해 전달되며, 이벤트는 Event store 컴포넌트를 통해 전달됩니다. Event store는 이벤트 버스 역할도 함께 수행합니다.

인프라 서비스

OrderShop v2에서 모든 유니캐스트 통신은 Message queue 컴포넌트를 통해 이루어집니다. 이를 위해 Redis Lists, 특히 두 개의 리스트를 결합한 소위 '신뢰할 수 있는 큐(reliable queue)'를 사용합니다. 이 큐는 단순 명령(예: 단일 엔티티 작업)은 동기적으로 처리하고, 장기 실행 작업(예: 배치 처리, 메일 발송)은 비동기적으로 처리하며, 동기식 메시지에 대한 응답도 기본적으로 지원합니다.

Event store는 Redis Streams를 기반으로 합니다. 도메인 서비스(OrderShop의 기능을 시연하기 위한 더미일 뿐입니다)는 이벤트 토픽, 즉 엔티티 이름을 딴 이벤트 스트림을 구독하고 해당 스트림에 이벤트를 게시합니다. 각 이벤트는 하나의 스트림 엔트리이며, 이벤트 타임스탬프가 ID 역할을 합니다. 스트림에 게시된 모든 이벤트의 총합이 곧 전체 시스템의 상태가 됩니다.

애플리케이션 서비스

Read model은 도메인 모델을 활용해 Event store에서 도출된 엔티티를 Redis에 캐싱합니다. 캐시를 제외하면 완전히 무상태(stateless)입니다.

API gateway 역시 무상태이며, 포트 5000에서 REST API를 제공합니다. HTTP 연결을 종료한 뒤, 상태 읽기(질의) 요청은 read model로, 상태 쓰기(명령) 요청은 전담 도메인 서비스로 라우팅합니다. 이처럼 읽기와 쓰기 작업을 개념적으로 분리하는 설계를 CQRS(Command Query Responsibility Segregation, 명령 질의 책임 분리) 패턴이라고 합니다.

도메인 서비스

도메인 서비스는 API gateway로부터 Message queue를 통해 쓰기 작업을 전달받습니다. 작업이 성공적으로 실행되면 각각에 대응하는 이벤트를 Event store에 게시합니다. 반면 모든 읽기 작업은 Event store에서 상태를 가져오는 Read model이 담당합니다.

CRM 서비스(고객 관계 관리 서비스)는 무상태로, 이벤트 스토어의 도메인 이벤트를 구독하고 Mail service를 통해 고객에게 이메일을 발송합니다.

핵심 도메인 엔티티는 주문(order)입니다. 주문에는 'status' 필드가 있으며, 그 상태 전환은 아래 다이어그램과 같이 상태 머신(state machine)을 통해 수행됩니다.

마이크로서비스 인프라에서 Redis 활용법: 메시지 큐와 이벤트 스토어 구축하기

이러한 상태 전환은 도메인 이벤트를 구독하는 여러 이벤트 핸들러(SAGA 패턴)에서 처리됩니다. 예를 들어 다음과 같습니다:

클라이언트

클라이언트는 Python의 Unit testing 프레임워크를 사용해 시뮬레이션됩니다. 현재 10개의 유닛 테스트가 구현되어 있으며, 자세한 내용은 tests/unit.py 파일을 참고하시기 바랍니다.

또한 포트 5000에서 WebSocket 기반의 간단한 UI가 제공되어, 이벤트를 실시간으로 감시하고 상태를 탐색할 수 있습니다.

RedisInsight 컨테이너도 함께 제공되어 Redis 인스턴스를 손쉽게 검사할 수 있습니다. 웹 브라우저에서 https://localhost:8001/ 에 접속한 후 redis:6379로 테스트 데이터베이스에 연결하세요.

마이크로서비스 인프라에서 Redis 활용법: 메시지 큐와 이벤트 스토어 구축하기

결론

Redis는 도메인 계층(예: 카탈로그 검색)과 애플리케이션 계층(예: HTTP 세션 스토어)에서 강력한 도구일 뿐만 아니라, 인프라 계층(예: 이벤트 스토어, 메시지 큐)에서도 탁월한 성능을 발휘합니다. 이렇게 여러 계층에서 Redis를 일관되게 사용하면 운영 오버헤드를 줄일 수 있고, 개발자는 이미 익숙한 기술을 재사용할 수 있습니다.

코드를 직접 살펴보고 구현해 보시기 바랍니다. 이 글이 Redis의 도메인 및 인프라 서비스 전반에서의 다재다능함과 유연성을 보여주고, 캐싱을 넘어선 활용 가능성을 입증하는 데 도움이 되기를 바랍니다.

Twitter(@martinez099)로 여러분의 경험을 공유해 주세요.