현재 스케일 아웃이 용이한 채팅 서버 설계를 위해서 스터디를 진행중이다. 단일 메시지 모델 설계를 위해 어떤 DB를 쓰고, 어떤 스택을 사용할 지 고민한 흔적을 소개해보려고 한다.
Reference
Best database for a chat application?
채팅을 위한 Message Queue 선택과 DB 선택
Kafka, Redis, Web Socket, Stomp 를 활용한 채팅 서버 회고
[혼자왔니] 채팅 서버 구현을 통해 알아본 redis와 kafka의 차이점
[WebSocket] Spring Boot + STOMP + Redis Pub/Sub 이용한 채팅 서버 구현
Spring Boot Web Chatting : 스프링 부트로 실시간 채팅 만들기(1) stomp, socketjs, websocket
[Spring] Gradle 프로젝트 생성 및 실행 (Intellij)
STOMP?
- WebSocket 프로토콜만 사용해서 채팅 서버만 구현한다면, 메시지 포멧 형식이나 통신 과정을 일일이 관리해야 하는 번거로움 → 메시지 처리에 최적화하기 위해 STOMP 사용
- STOMP는 메시지 송수신을 효율적으로 하기 위해 나온 프로토콜, WebSocket이 하위 프로토콜, STOMP가 상위 프로토콜
장점
- STOMP는 pub/sub 기반 동작하기 때문에, 메시지 송/수신에 대한 처리를 명확하게 정의 가능
- @MessagingMapping 어노테이션을 사용하여, 메시지 발행시 엔드포인트만 조정하여 쉽게 메시지 발/수신 가능
- 메시지 형식 정의 가능 → 클라이언트 서버간 통신에서 일관성 유지
- 메시지 브로커를 사용할 수 있어, 메시지 전송 시 중계 역할을 하는 서버를 따로 둘 수 있음.
- WebSocket 기반으로 각 connection마다, WebSocketHandler를 구현하는 것보다, @Controller 된 객체를 이용해 조직적 관리 가능
- 메세지는 STOMP의 "destination" 헤더를 기반으로 @Controller 객체의 @MethodMapping 메서드로 라우팅 된다.
- 메시지 보호 위해 Spring Security 사용 가능
STOMP Frame
COMMAND
header1:value1
header2:value2
Body^@
- COMMAND: 메시지 타입을 나타내는 문자열 (SEND, SUBSCRIBE, UNSUBSCRIBE)
- header1, header2 : 추가 정보를 제공하는 헤더(destination 헤더는 이 헤더로 메시지를 보내거나 구독 할 수 있다.)
- Payload(Body): 메시지 내용(끝은 NULL 문자로 설정)
Sub/pub 개념
- 채팅방 생성: pub/sub 구현을 위한 Topic 생성 → 채팅방 명 생성
- 채팅방 입장: Topic 구독(sub) → 해당 채팅방을 웹 소켓이 연결되어 있는 동안 구독함. 새로운 채팅이 송신(pub) 되면, 이를 수신(sub) 할 수 있음.
- 채팅방 메시지 수신: 해당 Topic로 메시지 송신(pub) → 해당 채팅방으로 메시지 송신한다.
인 메모리 브로커의 단점
- 확장성: 단일 서버 환경에서 작동하며, 메시지를 메모리에 저장하고 관리 → 세션 수 제한
- 결함 허용성: 장애 발생시 메시지 유실 가능성 높음
- 모니터링: 모니터링 하는 것이 불편
외부 메시지 브로커가 필요한 이유
- 확장성: RabbitMQ, 메시지 브로커는 클러스터링 및 분산 아키텍처 지원하여 세션수 확장 가능. 세션 수용량 한계 극복
- 결함 허용성: 메시지를 디스크에 저장하고, 필요한 경우 재전송 → 안전성 및 신뢰성
- 모니터링: RabbitMQ 관리자 도구를 통해 메시지 큐의 상태를 모니터링 하고, 웹 기반 인터페이스를 통해 세션 정보, 메시지 전송 및 수신 기록 확인 가능
- 느슨한 결합 (Publisher는 메시지를 발신할 때 다른 서비스들에 대해 알 필요가 전혀 없음, scale-out 용이)
외부 메시지 브로커 어떤 것을 선택할까?
Redis & Kafka
인메모리 데이터베이스 / 컴퓨터 메모리를 이용한(in-memory) Cache 서버
Key-Value를 이용해 Celery가 처리할 작업을 Celery에 보낸 후 Cache 에서 해당 Key를 제거하는 방식으로 작동한다.
- Redis는 데이터 검색을 위해 Cache를 가져다 쓴다는 점에서 속도가 빠르다.
- 매우 빠른 서비스 및 메모리 내 기능을 제공하기 때문에 지속성이 중요하지 않고 약간의 손실을 견딜 수있는 짧은 보존 메시지에 적합하다.
- 큰 메시지를 처리 할 때는 대기 시간이 오래 걸린다.
RabbitMQ
메시지 브로커
- 메시지를 다른 대기열로 보낼 수있는 라우팅 시스템을 갖추고 있다.
- 우선 순위가 높은 메시지를 먼저 사용하기 위해 작업자가 사용할 수 있는 메시지의 우선 순위를 지원한다.
- 메시지 브로커로서 Redis와 비교할 때 훨씬 더 다양한 기능을 제공한다.
- 크고 복잡한 메시지에 적합하다.
Kafka Redis
브로커 종류 | 이벤트 브로커(삭제 X) |
속도 | redis보다 느림(디스크 기반) |
topic | topic생성이 필요 |
삭제 작업 | 기본적으로 데이터가 삭제되지않고 따로 처리를 해야함 |
thread | multi |
scale-out | 가능( zookeeper 필요) |
무거운 정도 | 무거움 |
순서보장 | O |
설계
채팅 서비스의 고도화
- 서버 재시작 할때마다 채팅방 정보들 리셋 → 인메모리 보다, 빠른 읽기가 가능한 redis 사용
- 채팅 서버가 여러대일 때 서버간 채팅 방 공유 불가능 → 단순히 webSocket과 STOMP pub/sub를 이용하여 구현할 시, pub/sub이 발생한 서버 내에서만 메시지 주고 받는 것이 가능. (redis, rabbitMQ 등 별도의 메시지 브로커를 두어야 함)
요구 사항:
- WebSocket을 활용한 실시간 채팅 구현
- Stomp를 활용한 채팅 고도화 → pub/sub를 활용한 메시지 발신/수신
- Redis 사용
동작 과정
- 채팅방 생성 → redis 저장소에 채팅방 정보 저장
- 채팅방 입장시 → web Socket 연결 수행, 해당 채팅방에 대한 subscribe를 수행
- 해당 채팅방에 message request → message를 받아서 message queue(redis)에 publish
- message queue(redis)가 websocket 구독자에게 메시지 전달
- redis 사용 시 → message가 publish 되면 해당 channel을 구독하고 있는 모든 subscriber에게 메시지를 보내주게 된다. → 각기 다른 채팅 서버를 통해 접속했더라도 같은 채팅방에서 서로 대화 가능
- REDIS 서버가 죽어버리면 안에 있는 내용이 위험하고 채팅방의 채팅이 많아질 경우 채팅의 순서 보장 및 유실 가능성이 커진다.
redis → 라이브 다중 채팅
kafka → 1:1 채팅 프로그램, 순서보장 중요하며 redis만큼 빠르지 않아도 됨
Cassandra vs MongoDB
- Cassandra 노드가 추가될수록 MonogoDB 보다 훨씬 나은 선형적인 성능 향상을 보인다.
- 다중 Index가 필요한 구조라면 MongoDB를 선택하고, 데이터 항목 변경이 많고 unique access가 많은 경우라면 Cassandra가 적합
- noSQL이라 Read/write 속도가 빠름
- 결론: 웹 소켓 상단에 있는 STOMP를 사용하되, 메시지 브로커로는 Kafka를 사용하고, 빠른 읽기/쓰기가 가능한 Nosql 저장소인 Cassandra를 사용해보고자 한다!
'📗 BOOK > 가상 면접 사례로 배우는 대규모 시스템 설계 기초 정리' 카테고리의 다른 글
단일 메시지 브로커 설계하기 - (2) / 마이크로 서비스의 프로세스 간 통신 정리 (0) | 2024.01.20 |
---|---|
[가상면접 사례로 배우는 대규모 시스템 설계 기초] Chap3. 시스템 설계 공략법 (0) | 2023.11.29 |
[가상면접 사례로 배우는 대규모 시스템 설계 기초] Chap2. 개략적인 규모 추정 (0) | 2023.11.29 |
[가상면접 사례로 배우는 대규모 시스템 설계 기초] Chap12. 채팅 시스템 (1) | 2023.11.24 |
[가상면접 사례로 배우는 대규모 시스템 설계 기초] Chap01. 사용자 수에 따른 규모 확장성 (0) | 2023.11.16 |