12장: 채팅 시스템 설계

바인하·2022년 11월 14일
0

1단계: 문제 이해 및 설계 범위 확정

  • 일별 능동 사용자 수 기준으로 5천만명 처리
  • 그룹 채팅 : 최대 100명
  • 1:1 채팅, 그룹 채팅, 사용자 접속상태 표시 지원
  • 텍스트 메시지만 지원
  • 메시지 길이 100,000자 이하
  • 종단 간 암호화 지원

2단계: 개략적 설계안 제시 및 동의 구하기

  • 채팅 서비스의 경우 사용할 통신 프로토콜 결정하는 것이 중요한 문제
  • HTTP 프로토콜 사용
    • 송신 Client가 채팅 서비스에 HTTP 프로토콜로 연결한 후 메시지를 보냄
    • 수신 Client에게 해당 메시지를 전달하라고 알림
  • 채팅 서비스와의 접속에는 keep-alive 헤더가 효율적
    • 클라이언트 - 서버 사이의 연결을 끊지 않고 계속 유지 가능
    • TCP 접속 과정에서 발생하는 핸드셰이크 횟수 줄일 수 있음
  • HTTP는 클라이언트가 연결을 만드는 프로토콜인데, 서버에서 연결을 만드는 것처럼 동작하는 많은 기법들이 존재함! ex) 폴링, 롱 폴링, 웹 소켓

폴링

  • Client가 주기적으로 Server에게 새 메시지가 있냐고 물어보는 방법
  • 단점
    • 폴링을 자주 할수록 폴링 비용 상승
    • 답해줄 메시지가 없는 경우, 서버 자원이 불필요하게 낭비됨

롱 폴링

  • Client가 새 메시지가 반환되거나, 타임아웃 될 때까지 연결 유지
  • 새 메시지를 받으면 기존 연결 종료, 서버에 새로운 요청을 보내어 모든 절차 재시작
  • 단점
    - 메시지 송신 Client와 수신 Client가 같은 채팅 서버에 접속하지 않을 수도 있음.
    HTTP 서버들은 보통 무상태 서버이므로, 로드밸런싱을 위한 라운드 로빈 알고리즘을 사용한다면, 메시지 송신 서버 != 수신 서버의 가능성이 있음
    • 서버 입장에서 클라이언트의 연결 해제 여부를 알 방법이 없음
    • 여전히 타임아웃 문제 존재

웹소켓

  • 서버가 클라이언트에게 비동기 메시지를 보낼 때 널리 이용하는 기술
  • 웹소켓 연결의 시작은 클라이언트. 한번 맺어진 연결은 영구적 양방향
  • 처음은 HTTP 연결이지만, 특정 핸드셰이크 절차를 거쳐 웹소켓 연결로 업그레이드
  • 영구적인 연결이 만들어지면 서버는 클라이언트에게 비동기적 메시지 전송이 가능

개략적 설계안

  • 무상태 서비스 : 로그인/회원가입/사용자 프로파일 표시를 처리하는 전통적인 요청/응답 서비스
    - 서비스 탐색 : 클라이언트가 접속할 채팅 서버의 DNS 호스트명을 클라이언트에게 알려주는 역할
  • 상태 유지 서비스 : 채팅 서비스 (각 클라이언트가 채팅 서버와 독립적인 네트워크 연결 유지해야 하므로)
    - 서비스 탐색 서비스는 채팅 서비스와 협력하여 특정 서버에 부하가 몰리지 않도록 함
  • 제3자 서비스 연동 : 푸시 알림

▼ 통합한 설계안

저장소

  • 어떤 DB를 쓰느냐는, 데이터의 유형과 읽기/쓰기 연산의 패턴에 따라 달라짐
  • 채팅 시스템이 다루는 데이터
  1. 사용자 프로파일, 설정, 친구목록과 같은 일반 데이터 -> 안전성 보장하는 RDBMS에 보관
  2. 채팅 이력
    - 매일 600만개의 대량의 메시지 처리
    - 최근에 주고받은 메시지 확인
    - 검색 기능, 멘션된 메시지 확인을 위해 특정 메시지로 점프, 무작위 데이터 접근
    - 1:1 채팅 앱의 경우, READ:WRITE = 1:1

    위의 요구사항을 모두 만족하는 DB는 키-값 저장소
  • 수평적 규모 확장이 용이
  • 데이터 접근 지연시간이 낮다
  • RDB는 인덱스가 커지면 데이터에 대한 무작위적 접근 처리 비용이 늘어남

1:1 대칭 테이블
1. 메시지 ID
2. 받는 사람
3. 보내는 사람
4. 내용
5. 생성 일시

그룹 채팅 테이블 -> 1,2 를 복합키 기본 키로 사용
1. 채널 ID (채팅 그룹) -> 파티션 키로 사용
2. 메시지 ID
3. 받는 사람
4. 내용
5. 생성 일시

메세지 ID

요구사항
1. 메시지 ID 값은 고유해야 한다
2. ID 값은 정렬 가능한다
- RDBMS 라면 auto_increment 가능하지만 NoSQL은 보통 해당 기능 제공X
- 스노플레이크 같은 64bit 순서 번호 생성기를 이용
- 지역적 순서 번호 생성기 : ID의 유일성을 같은 그룹 안에서만 보증하면 충분하다

3단계 : 상세 설계

  • 서비스 탐색 : 클라이언트가 접속할 채팅 서버의 DNS 호스트명을 클라이언트에게 알려주는 역할
    • 클라이언트에게 가장 적합한 채팅 서버를 추천하는 것
    • 기준 : 클라이언트의 위치, 서버의 용량
    • 이를 구현하는 오픈 소스 솔루션 : 아파치 주키퍼

→ 사용 가능한 모든 채팅 서버 등록 후, 클라이언트가 접속 시도하면 사전에 정한 기준에 따라 최적의 채팅 서버 선택


1. A가 시스템에 로그인 시도
2. 로드밸런서가 로그인 요청을 API 서버에 보낸다.
3, API 서버가 인증 처리 후 서비스 탐색 기능을 통해 최적의 채팅 서버를 찾는다. 최적의 채팅 서버로 2를 반환한다고 가정

  • A는 채팅 서버 2와 웹 소켓 연결

메시지 흐름


1. A가 채팅 서버 1로 메시지 전송
2. 채팅 서버 1은 ID 생성기로 메시지 ID 결정
3. 채팅 서버 1은 해당 메시지를 메시지 동기화 큐로 전송
4, 메시지를 키-값 저장소에 저장
5.a. B가 온라인인 경우, 메시지는 채팅 서버 2로 전송됨.
5.b. 오프라인인 경우, 메시지를 푸시 알림 서버로 전송
6. B와 채팅서버 2는 웹소켓으로 연결되어 있으며, 채팅서버 2가 B에게 메시지 전송.

소규모 그룹 채팅에서의 메시지 흐름

  • A가 보낸 메시지는 B,C 의 메시지 동기화 큐에 복사된다.
  • 그룹이 크지 않은 경우 메시지를 수신자별로 복사
  • 각 사용자의 수신함은 여러 사용자로부터 오는 메시지를 받을 수 있어야 함

접속상태 표시

  • 사용자 로그인
    - 클라이언트와 실시간 서비스 사이에 웹소켓 연결이 맺어지면, 접속상태 서버는 A의 상태와 최근활동일시를 키-값 저장소에 보관
    • 이 작업 이후 해당 사용자는 접속중인 것으로 표시
  • 접속 장애
  • 사용자 인터넷 연결이 끊어지면, 사용자를 오프라인 상태로 표시 후 연결 복구되면 온라인 상태로 변경
    -> 하지만 너무 잦은 변경은 지나침
    -> 박동 검사로 해결
    박동검사 : 온라인 상태의 클라이언트가 주기적으로 박동 이벤트를 접속상태 서버로 보내게 하고, 마지막 이벤트를 받은 지 N초 인애ㅔ 또 다른 박동이벤트 메시지를 받으면 해당 사용자의 접속 상태를 계속 온라인으로 유지

상태정보 전송

  • 상태정보 서버 : 발행-구독 모델 사용
    • 각각 친구관계마다 채널을 하나씩 생성
      ex) A 접속상태 변경 시 A-B, A-C, A-D 3개의 채널에 사용

4단계 : 마무리

  • 추가 논의 사항
  1. 미디어 파일을 지원하는 방법
    • 미디어 파일 압축 방식, 클라우드 저장소, 썸네일 생성
  2. 종단 간 암호화
  3. 캐시
  4. 로딩 속도 개선
    • 슬랙은 사용자 데이터, 채널 등을 지역적으로 분산하는 네트워크를 구축하여 앱 로딩 속도 개선
  5. 오류 처리
    • 채팅 서버 오류: 채팅 서버가 죽으면 서비스 탐색 서비스가 클라이언트에게 새로운 서버를 배정하고 재접속 할 수 있도록.
    • 메시지 재전송: 재시도, 큐
profile
되면 한다

0개의 댓글