📗 개발자 책 읽기/가상 면접 사례로 배우는 대규모 시스템 설계 기초

[System Design Interview] 12. 채팅 서버 시스템 설계하기

민돌v 2023. 5. 30. 10:56
728x90
가상 면접 사례로 배우는 대규모 시스템 설계 기초 (System Design Interview) - 저 : 알렉스 쉬, 역 : 이병준 을 읽고 정리한 글입니다.

 

12장에서는 채팅시스템을 설계해 봅니다.  설계하고자 하는 요구사항은 아래와 같습니다.

  1. 응답지연이 낮은 1:1 채팅
  2. 그룹 채팅(최대 100명) 지원
  3. 다양한 단말 지원 (앱, 웹). 하나의 계정으로 여러 단말에 동시 접속 가능
  4. 사용자 접속상태 표시
  5. 채팅 이력 보관

 

 

[목차]

  1. 클라이언트 통신
  2. 채팅 서비스 개략적 설계
  3. 채팅 서비스의 데이터 저장소
  4. 메시지 흐름 과정

 

 


📌 01. 클라이언트 통신

클라이언트는 서로 직접 통신하지 않습니다. 각 클라이언트는 위에 나열한 모든 기능을 지원하는 "채팅 서비스"와 통신합니다.

채팅을 시작하려는 클라이언트는 네트워크 통신 프로토콜을 사용하여 서비스에 접속합니다.

  1. 메시지 송신 클라이언트(sender) 가 클라이언트/서버 어플리케이션에 요청을 보냅니다.
  2. 메시지 송신 클라이언트(sender)는 채팅 서비스에 HTTP 프로토콜로 연결한 다음 메시지를 보내어 수신자에게 해당 메시지를 전달하라고 알립니다.
    • HTTP 프로토콜을 사용할 경우 "keep-alive" 헤더를 사용하면 클라이언트와 서버 사이의 연결을 끊지 않고 계속 유지해 TCP 접속 과정에서 발생하는 handshake 횟수를 줄일 수 있어 효과적입니다. 

 

HTTP 로 효과적인 실시간 연결을 보장하기 위해 많은 기술을 고안해 왔는데 "폴링, 롱폴링, 스트리밍, 웹소켓 등" 과 같은 기술이 존재합니다.

  1. 폴링 (polling)
    • 수신받은 클라이언트 (receiver)가 주기적으로 채팅 서비스(서버) 에게 수신받을 메세지가 존재하냐고 물어보는 방식입니다.
    • 폴링 비용은, 폴링을 자주할수록 증가 됩니다.
  2. 롱 폴링 (long polling)
    • 폴링의 개선점으로 나온 방식이 롱 폴링 입니다.
    • 롱 폴링은 클라이언트가 새 메시지가 반환되거나, 타임아웃이 될 때 까지 연결을 유지합니다.
    • 클라이언트는 새 메시지를 받으면 기존 연결을 종료하고 서버에 새로운 요청을 보내어 모든 절차를 다시 시작합니다.
      • 단점
        1. HTTP 서버는 보통 무상태(stateless) 하기 때문에 로드밸런싱을 위해 라운드 로빈(round robin) 알고리즘을 사용하는 경우 메시지를 받은 서버는 해당 메시지를 수신할 클라이언트와의 롱 폴링 연결을 가지고 있지 않은 서버일 수 있다.
          즉, 메시지를 보내는 클라이언트(sender) 와 수신받는 클라이언틍(receiver)가 같은 채팅 서버에 접속하게 되지 않을 수 도 있기 때문에 롱-폴링 비용을 낭비할 수 도 있다.
        2. 서버 입장에서는 클라이언트가 연결을 해지했는지 알 수가 없다.
        3. 메시지를 많이 받지 않는 클라이언트도 타임아웃이 일어날 때 마다 주기적으로 서버에 다시 접속하기 때문에 여전히 비효율적이다.
  3. 웹 소켓 (websocket)
    • 웹소켓은 서버가 클라이언트에게 비동기 메시지를 보낼 떄 가장 널리 사용되는 기술입니다.
    • 웹소켓으로 맺어진 연결은 항구적(permanent)이며 양방향성 입니다.
    • 과정
      1. 웹소켓 연결은 클라이언트가 처음 시작합니다. HTTP 연결로 시작하지만 특정 핸드셰이크 절차를 거쳐 웹소켓 연결로 업그레이드 됩니다.
      2. 웹소켓으로 항구적인 연결이 만들어지면, 서버는 클라이언트에게 비동기적으로 메시지를 전송할 수 있습니다.
    • 특징
      1. 웹소켓은 HTTP or HTTPS 프로토콜일 사용하는 기본 포트번호를 그대로 쓰기 때문에 일반적으로 방화벽이 있는 환경에서도 잘 작동합니다.
      2. 웹소켓을 이용하면 메시지를 보낼때와 받을 떄 동일한 프로토콜을 사용할 수 있으므로 설계와 구현이 단순하고 직관적이기됩니다.
    • 단, 웹소켓 연결은 항구적으로 유지되어야하기 때문에 서버측에서 연결관리를 효율적으로 해야합니다.

웹소켓 과정

 


 

📌 02. 채팅 서비스 계략적 설계안

채팅 시스템은 무상태 서비스, 상태 유지(statefull) 서비스, 제 3자 서비스 연동의 세 부분으로 나누어서 살펴볼 수 있습니다.

1) 무상태 서비스

  • 무상태 서비스는 기본의 로그인, 회원가입, 사용자 프로파일 표시 등을 처리하는 전통적인 request/response 서비스 입니다.
  • 무상태 서비스가 제공하는 기능은 많은 웹사이트와 앱이 보편적으로 제공하는 api 서버를 의미합니다.
  • 무상태 서비스는 로드밸런스 뒤에 위치하여, 로드밸런서가 사용자의 요청을 그 경로에 맞는 서비스로 전달합니다.
  • 무상태 서비스는 모놀리틱(monolithic) 서비스 일수도 있고, 마이크로 서비스 일 수 있습니다.

 

2) 상태 유지 서비스

  • 채팅 서비스 입니다.
  • 각 클랑이언트가 채팅 서버와 독립적인 네트워크 연결을 유지해야 하기 때문에 설계안에서 유일하게 상태 유지가 필요합니다.
  • 클라이언트는 보통 서버가 살아 있는 한 다른 서버로 연결을 변경하지 않습니다.

 

3) 제 3자 서비스 연동

  • 푸시 알림 서비스 입니다.
  • 새 메시지를 받았다는 앱이 실행중이지 않더라도 알림을 받아야해서 별도의 서버로 분리합니다.
  • 이에 관련된 사항은 10장 알림시스템 설계 해당 포스팅을 참고해주세용

 

 

여기서 트래픽에 의한 규모 확장성을 생각하여 1대의 서버가 아닌, 여러 서버로 구성한다고 하면 아래와 같은 설계안이 나옵니다.

  • 채팅서버는 클라이언트 사이에 메시지를 중계하는 역할을 담당합니다.
  • 접속상태 서버(presence server)는 사용자 접속 여부를 관리합니다.
  • API 서버는 로그인, 회원가입, 프로파일 변경 등 그 외 나머지 전부를 처리합니다.
  • 알림 서버는 푸시 알림을 보냅니다.
  • 키-값 저장소에는 채팅 이력(chat-history)를 보관합니다. 시스템에 접속한 사용자는 이전 채팅 이력을 전부 보게됩니다.

 


 

📌 03. 채팅 서비스의 데이터 저장소

채팅시스템이 다루는 데이터는 보통 2가지 입니다.

  1. 사용자 프로파일, 설정, 친구 목록 등 일반적인 데이터
  2. 채팅이력과 같은 채팅 시스템 고유 데이터

 

✔️ 1) 번 유형은 보통 안전성을 보장하는 RDB(관계형 데이터베이스)를 사용합니다. 다중화(replication) 과 샤딩(sharding)은 이런 데이터의 가용성과 규모확장성을 보증하기 위한 보편적인 기술입니다.

 

✔️ 2)번 유형은 키-값 저장소 (key-value) 데이터베이스 추천합니다.

  1. 키-값 저장소는 수평적 규모확장이 쉽습니다.
  2. 키-값 저장소는 데이터 접근 지연시간이 낮기 때문에 채팅이력 탐색에 유리합니다.
  3. 관계형 데이터베이스는 데이터 가운데 롱 테일(long-tail)에 해당하는 부분을 잘 처리하지 못하는 경향이 있다고 합니다. 인덱스가 커지면 데이터에 대한 무작위적 접근을 처리하는 비용이 늘어나기 때문입니다.

 


 

📌 04. 메세지 흐름 이해하기

채팅 시스템에서의 종단 기기간 메세지가 전송되고 수신되는 과정을 살펴봅시다.

 

1) 1:1 채팅 메시지 처리 과정

  1. 사용자 A 가 채팅 서버 1로 메시지를 전송합니다.
  2. 채팅 서버 1은 ID 생성기를 사용하여 해당 메세지의 ID를 결정합니다. (데이터 생성)
  3. 채팅 서버 1은 해당 메시지를 메시지 동기화 큐로 전송합니다.
  4. 메시지가 키-값 저장소에 보관됩니다.
  5. (a) 사용자 B가 접속 중인 경우 메시지는 사용자 B가 접속 중인 채팅 서버(본 예제의 경우에는 채팅서버 2)로 전송됩니다.
    (b) 사용자 B가 접속중이 아니면 푸시 알림 메시지를 푸시알림 서버로 보냅니다.
  6. 채팅 서버 2는 메시지를 사용자 B에게 전송. 사용자 B와 채팅 서버 2 사이에는 웹소켓 연결이 있는 상태이므로 그것을 이용하여 메시지를 수신합니다.

 


 

2) 그룹 채팅 메시지 처리 과정

3명의 사용자 A, B, C가 그룹채팅을 한다고 가정했을 시, 메시지 수신되는 흐름 입니다.

  1. 사용자 A가 보낸 메시지가 사용자 B와 C의 메시지 동기화 큐(message-sync queue) 에 복사됩니다.
  2. 각 사용자는 새로운 메시지가 왔는지에 대해 자기 큐를 확인하여 새로운 메시지를 수신합니다.

 

단 이런 소규모가 아닌 500명 이상의 대규모 그룹채팅이 가능할 경우, 모든 사용자의 큐에 똑같은 메세지를 복사하는 것은 바람직하지 않을 수 있습니다.

따라서 한 수신자는, 여러 사용자로부터 오는 메시지를 수신하는 별도의 개인 수신함(메시지 큐)를 가지고 있는것이 바람직합니다.
즉, 각 사용자의 메시지 동기화 큐는 아래 그림처럼 여러 사용자로부터 오는 메시지를 받을 수 있어야합니다.

 

 

 

+ 추가적으로

접속상태는 유저의 로그인, 로그아웃으로 상태를 표시할 수도 있지만, 네트워크 오류 등과 같은 접속장애나 앱 사용자 같은경우 백그라운드 돌렸을 때 무한정 로그인 상태가 될 수 도있습니다.

이러한 경우를 체크하기위해 박동 이벤트라는 기술이 있는데 저는 자세한 내용은 다루지 않겠습니다 

 

 

 

끝!

 

 

 

반응형