JUN0.DEV
JUN0.DEV

멀티 인스턴스 환경에서 채팅 메시지 전파하기: Redis Pub/Sub

Published on
  • avatarJunyoung Yang
GitHubkakao-tech-campus-3rd-step3/Team12_BEUniSchedule 백엔드 레포지토리

카카오 테크 캠퍼스 최종 프로젝트에서 UniSchedule - 대학생을 위한 일정 관리 서비스를 만들면서 팀 일정 관리뿐 아니라 팀원들이 소통할 수 있는 채팅 기능도 함께 구현하게 되었다.

처음의 단일 인스턴스 환경(로컬이나 테스트 서버)에서는 채팅 메시지 전송이 자연스럽게 동작했다. 하지만 운영서버인 AWS ECS/Fargate 환경에서는 인스턴스가 여러 개이므로 문제가 발생했다. 사용자마다 서로 다른 서버 인스턴스에 연결될 수 있었고, 이 경우 한 인스턴스에서 발생한 채팅 메시지가 다른 인스턴스에 연결된 사용자에게는 전달되지 않는 문제가 발생했다.

해당 문제를 해결하기 위해 Redis Pub/Sub를 적용하게 되었다.

기존 구현

팀 채팅 기능을 구현하기 위해 Spring Boot에서 단순 WebSocket을 사용하여 기능을 구현하였다.

단일 인스턴스 환경에서의 테스트

단일 인스턴스 환경(로컬 환경이나 테스트 서버)에서는 문제가 발생하지 않았다.

채팅 기능의 기본 흐름은 다음과 같다.

  • 사용자가 서버에 연결 (WebSocket)
  • 사용자가 메시지를 전송
  • 서버가 메시지를 수신
  • 서버가 같은 채팅방에 있는 사용자들에게 메시지를 전달

따라서 단일 인스턴스에서는 모든 WebSocket 연결이 당연히 하나의 서버에 붙어 있기 때문에 자연스럽게 잘 동작했다.

하지만 이 구조는 여러 인스턴스(AWS ECS)로 구성된 운영 서버에는 통하지 않았다.

멀티 인스턴스 환경(AWS ECS/Fargate)에서의 문제

멀티 인스턴스 환경에서는 다음 상황이 발생한다.

  • 사용자 A는 인스턴스 1에 연결
  • 사용자 B는 인스턴스 2에 연결
  • A가 메시지를 전송
  • 인스턴스 1이 메시지를 처리
  • 인스턴스 2는 해당 사실을 전혀 모름

이 경우 인스턴스 1에 연결된 사용자에게만 메시지가 전달되고, 인스턴스 2에 연결된 사용자에게는 메시지가 보이지 않게 된다. 따라서, 사용자 입장에서는 같은 채팅방을 사용하고 있음에도 불구하고 일부 사용자(동일 인스턴스)와만 소통이 되는 문제가 발생한다.

해결 방안

이 문제를 해결하기 위해 Redis Pub/Sub을 도입했다. DB에 채팅 데이터를 저장해 인스턴스 간 공유하는 방식도 생각했었지만, 로드가 커보였고, Redis Pub/Sub을 통해 메시지 발생 사실을 인스턴스 간에 전파하기만 하면 훨씬 간단히 해결할 수 있을 것 같았다. 해당 내용을 ADR로 정리해 팀에도 공유하였다.

Redis Pub/Sub이란?

Redis의 핵심 기능 중 하나로, 특정한 주제(topic)에 대하여 해당 topic을 구독한 모두에게 메시지를 발행하는 통신 방법으로, 채널을 구독한 수신자(클라이언트) 모두에게 메세지를 전송할 수 있는 기능이다. pubsub.png

https://redis.io/docs/latest/develop/pubsub/

적용 및 구현

Redis Pub/Sub이 적용된 흐름은 다음과 같다.

  • 사용자가 메시지를 전송한다.
  • 메시지를 받은 인스턴스가 Redis 채널에 메시지를 발행한다. (모든 인스턴스는 해당 채널을 이미 구독하고 있다)
  • 각 인스턴스는 채널에서 메시지를 수신한 뒤, 자신에게 연결된 사용자들에게 전달한다.

이 구조를 통해 특정 인스턴스에서 발생한 메시지도 모든 인스턴스로 전파될 수 있었다.

마무리

UniSchedule에서 채팅 유실 문제를 겪은 이후, 이전에는 서버를 늘리는 것을 단순 성능 확장으로 생각했지만, 실제로는 서버 수가 늘어날수록 이벤트 전파와 상태 공유 구조를 미리 고민하고 함께 설계해야 한다는 점을 깨달았다.

단일 인스턴스에서는 자연스럽게 동작하던 흐름이 멀티 인스턴스에서는 바로 어긋날 수 있다는 것을 직접 경험했고, 이후에는 초기부터 멀티 인스턴스 환경에서 잘 동작할 수 있는 구조에 대해서도 고민해보게 되었다.