메신저 시스템 디자인 해보기

Ssol·2023년 4월 21일
0
post-thumbnail

유튜브에서 영상을 보던 도중 신묘한 알고리즘이 추천해주는 가상의 시스템을 설계해보는 영상을 본 적이 있다.
이번 글에는 그 내용을 한번 요약 정리해보고자 한다.

카카오톡 시스템 디자인 해보기

스펙

  • 하루에 대략 600억개의 메시지가 오고감
  • 사용자들은 오래된 채팅은 잘 안봄
  • READ/WRITE 비율이 1:1

클라이언트와 서버의 HTTP 커넥션 문제

보통 서버와 통신을 할 땐 HTTP 통신을 많이 사용한다.
하지만 HTTP 요청은 무조건 클라이언트가 먼저 시작해야 되는 것이 문제.
A가 B에게 메시지를 보내면 그 메시지는 서버에 전달이 되지만 서버에 아무 요청을 하지 않은 B에겐 서버가 먼저 응답을 할 수 없다.

이 문제를 해결하기 위한 몇가지 옵션을 찾아보자.

1. Polling

새로운 내용의 메시지가 있는지 주기적으로 서버에 계속 물어보는 것을 말한다.

메시지 왔니?
아니요
메시지 왔니?
아니요
이번엔 메시지 왔니?

이 방식은 request 수가 너무 많아지게 되어 리소스 낭비가 되고, 메시지에 요청 주기만큼 레이턴시가 걸리게 된다.

2. Long Polling

Polling 방식과 유사한데 단지 서버가 요청을 받고나서 메시지가 없다면 바로 메시지가 없다고 응답을 주는게 아니라 일정 시간만큼의 타임아웃까지 기다린 후 응답을 보내고, 만약 기다리는 도중 메시지가 있다면 바로 응답하는 방식이다.

메시지 왔니?
... 아니요
메시지 왔니?
.. 어? 방금 왔어요!

Polling에 비해 request 수는 줄지만 여전히 많은 request가 가긴 한다. 그리고 레이턴시도 마찬가지로 존재하고.

3. Web Socket

클라이언트와 서버 사이에 오픈 커넥션을 유지하는 방법이다. 그렇기 때문에 양방향 소통이 가능해진다.

그래서 카카오톡 같은 1:1 채팅에선 웹소켓을 사용한다.

  • 웹 소켓은 오픈 커넥션을 계속 유지해야하기 때문에 이 오픈 커넥션을 유지할 chat server를 따로 만들어서 관리한다.
  • 일반적인 request(로그인, 프로필 변경 등)는 API 서버에서 HTTP로 관리한다.

메시지 큐(Message Queue)란?

서비스들 간에 데이터를 주고 받는 방법 중에 하나이다.
MSA에서 두 개의 서비스 간에 메시지 큐를 사용하지 않고 이야기하는 방식인 REST API나 RPC를 사용하면 Synchronous 하게 데이터를 주고 받음.

메시지 큐를 사용하면 Asynchronous하게 데이터를 처리할 수 있다.
메시지 큐는 두가지 역할을 통해 작동하는데 하나는 이벤트 발생시 메시지 큐에 전달해주는 Publisher와 이 이벤트가 메시지 큐에 들어왔을 때 알림을 받는 Subscriber이다.
대표적인 서비스로는 Kafka, RebibtMQ가 있다.

Service B가 Topic 1이라는 이벤트에 대해 Subscribe를 하고 있어서 Service A가 Topic 1 이벤트를 메시지 큐로 전달 했을 때, 메시지 큐가 Service B에 구독하고 있는 이벤트가 발생했다고 알려주는 것이다.

REST API/RPC 대신 왜 메시지 큐를 쓸까?

여러 장점이 있는데 그 중 대표적인 것이 Decoupling이다.

Decoupling

마이크로 서비스들이 많아지다 보면 서비스 A에 의존적인 서비스가 많을 수 있겠지? 이 서비스 ㅁ에 대한 요청을 REST API/RPC로 Synchronous하게 하려면 모든 디펜던시에 대한 코드를 서비스 A에 넣어야 한다.

이렇게 되면 서비스 A는 점점 복잡해지고 테스트도 어려워질 것이다. =시스템 디자인적으로 굉장히 안좋음

이런 상황에서 메시지 큐를 사용하도록 하면

디펜던시가 확 줄게 된다.
왜냐하면 서비스 A에 대한 의존성을 가진 다른 서비스들이 존재한다느 것을 알 필요가 없어지기 때문이다. 서비스 A의 역할은 메시지 큐에 이미지를 보내주는 것으로 끝인 것이다.(메시지 큐를 도입한 후 서비스 A의 디펜던시는 메시지 큐 1개가 되었다.)
각각의 서비스는 메시지 큐만 알면 됨.

이제 메시지가 전달되는 흐름을 한번 살펴보자.

유저A가 메시지를 보내면 웹소캣으로 chat server 1로 들어간다. 이 메시지는 유저B에게 도착해야 한다. 그러니 유저B의 메시지 큐에 넣어준다. 메시지 큐의 유저B 이벤트를 구독하고 있던 서비스들에게 알림이 간다. DB에도 유저A가 보낸 메시지가 저장이 되고, 유저B가 접속해 있는 chat server 3으로 전달되서 웹소켓으로 유저B에게 전달 되는 것이다.

데이터베이스는 어떤 것을 써야 할까?

트래픽 특성

  • 엄청난 양의 트래픽 그룹 채팅 - 하루 600억개
  • 오래된 채팅 잘 안봄
  • READ/WRITE 비율 1:1

다른 데이터들과 join 할 필요가 거의 없음
RDBMS 같은 경우는 데이터가 많아지고 index가 많아지면 느려짐

그래서 Key Value Store 사용

Key Value Store의 장점으로는

  • 스케일 하기 편함
  • Read 레이턴시가 낮음
  • 페이스북은 HBase, 디스코드는 Cassandra를 사용하는 등 실제 운영 사례도 많음

Key를 만들 땐 Range Scan 하기 쉽게 디자인 해야 한다.

  • 최근 보낸 메시지일수록 Key 값이 높게

만약 여기에 그룹 챗 기능을 추가한다면?

그룹 챗을 몇명까지 지원할 것인가에 따라 디자인이 달라질 수 있음
최대 200명까지라고 가정을 한다면 어느정도의 Fan out은 괜찮다.

  1. 유저A가 그룹 챗에 메시지를 보냄
  2. chat server가 이 그룹 소속 멤버를 조회하기 위해 Group Chat DB를 찌름(이 DB는 RDBMS여도 상관 없다.)
  3. 받아야 할 멤버들의 큐에 메시지를 넣어줌
  4. 이후 나머지는 1:1 채팅과 똑같다
profile
Junior Back-end Developer 🫠

0개의 댓글