728x90
가상 면접 사례로 배우는 대규모 시스템 설계 기초 "채팅 시스템 설계"을 요약한 내용입니다.
1단계 문제 이해 및 설계 범위 확정
- 응답 지연이 낮은 일대일 채팅 기능
- 최대 100명까지 참여할 수 있는 그룹 채팅 기능
- 사용자의 접속상태 표기 기능
- 다양한 단말 지원, 하나의 계정으로 여러 단말에 동시 접속 지원
- 푸시 알림
- 5천만 DAU를 처리할 수 있도록 할 것
2단계 개략적 설계안 제시 및 동의 구하기
- 채팅 서비스는 아래 기능을 제공
- 클라이언트로부터 메시지 수신
- 메시지 수신자 결정 및 전달
- 수신자가 접속 상태가 아닌 경우에는 접속할 때까지 해당 메시지 보관
폴링
- 폴링은 클라이언트가 주기적으로 서버에 새 메시지가 있는지 확인
- 폴링 비용은 폴링을 자주하면 할수록 올라간다
- 답해줄 메시지가 없는 경우에는 서버 자원이 불필요하게 낭비됨
롱 폴링
- 폴링은 여러가지로 비효율적일 수 있어서 나온 기법이 롱 롤링(Long Polling)이다
- 클라이언트는 새 메시지가 반환되거나 타임아웃 될 때까지 연결을 유지
- 새 메시지를 받으면 기존 연결을 종료하고 서버에 새로운 요청을 보냄
단점
- 메시지를 보내는 클라이언트와 수신하는 클라이언트가 같은 채팅 서버에 접속하게 되지 않을 수 있다.
- 메시지를 받은 서버는 해당 메시지를 수신할 클라이언트와 롱폴링 연결을 가지고 있지 않은 서버일 수 있다.
- 서버 입장에서는 클라이언트가 연경을 해제했는지 아닌지 알 좋은 방법이 없다.
웹소켓
- 서버가 클라이언트에게 비동기 메시지를 보낼 때 가장 널리 사용하는 기술
- 한번 맺어진 연결은 항구적이며 양방향이다.
- 처음에는 HTTP 연결이지만 특정 핸드셰이크 절차를 거쳐 웹소켓 연결로 업그레이드 된다.
- 항구적인 연결이 만들어지고 나면 서버는 클라이언트에게 비동기적으로 메시지를 전송할 수 있다.
- 웹소켓은 일반적으로 방화벽이 있는 환경에서도 잘 동작한다.
- 80이나 443처럼 HTTP 혹은 HTTPS 프로토콜이 사용하는 기본 포트번호를 그대로 쓰기 때문
- 웹소켓은 양방향 메시지 전송이 가능하므로 웹소켓 대신 HTTP를 굳이 고집할 이유는 없음
- 유의할 것은 웹소켓 연결은 항구적으로 유지되어야 하기 때문에 서버측에서 연결 관리를 효율적으로 해야함
개략적 설계안
- 채팅 기능의 경우 웹소켓을 사용하는 것이 효율적임
- 그외 대부분의 기능(회원가입, 로그인, 사용자 프로파일 등)은 일반적인 HTTP에서 구현해도 됨
- 전체 시스템의 개략적 설계안을 살펴보자
- 채팅 시스템은 세 부분으로 나누어짐
- 무상태 서비스, 상태유지 서비스, 제 3자 서비스 연동
무상태 서비스
- 로그인, 회원가입, 사용자 프로파일 표시 등의 처리하는 서비스
- 무상태 서비스는 로드밸런서 뒤에 위치
- ‘서비스 탐색(Service Discovery)’ 서비스는 클라이언트가 접속할 채팅 서버의 DNS 호스트명을 클라이언트에게 알려주는 역할
상태 유지 서비스
- 유일하게 상태 유지가 필요한 서비스는 채팅 서비스
- 각 클라이언트가 채팅 서버와 독립적인 네트워크 연결을 유지해야 하기 때문
- 클라이언트는 서버가 살아 있는 한 다른 서버로 연결을 변경하지 않음
- 서비스 탐색 서비스는 채팅 서비스와 긴밀히 협력하여 특정 서버에 부하가 몰리지 않도록 한다.
제 3자 서비스 연동
- 제 3자 서비스는 푸시 알림이다.
- 새 메시지를 받았다면 앱이 실행 중이지 않더라도 알림을 받아야 한다.
- ‘알림 시스템 설계’ 참고
규모 확장성
- 트래픽 규모가 얼마 되지 않을 때는 모든 기능을 서버 한대로 구현할 수 있음
- 면접에서 원하는 답변은 아닐 것
- 보통은 규모가 있는 트래픽을 서버 한 대로 처리하려고 하지 않을 것
- SPOF(Single Point Of Failure)가 발생 할 수 있음
- [그림 12-8]
- 실시간으로 메시지를 주고받기 위해 클라이언트는 채팅 서버와 웹소켓 연결을 끊지 않고 유지 해야함
- 채팅 서버는 클라이언트 사이에 메시지를 중계하는 역활을 담당
- 접속상태 서비스는 사용자의 접속 여부를 관리
- API 서버는 로그인, 회원가입, 프로파일 변경 등 그외 나머지 전부를 처리
- 알림 서버는 푸시 알림을 보낸다
- 키-값 저장소에는 채팅 이력을 보관
- 시스템에 접속한 사용자는 이전 채팅 이력을 전부 보게 될 것
저장소
- 관계형 데이터베이스를 쓸것 인가 아니면 NoSQL을 채택할 것인가?
- 중요하게 따져야할 것은 데이터의 유형과 읽기/쓰기 연산의 패턴
- 채팅 시스템이 다루는 데이터는 보통 두가지 이다.
- 사용자 프로파일, 설정, 친구 목록 처럼 일반적인 데이터
- 관계형 데이터베이스에 보관
- 채팅 이력
- 채팅 이력 데이터의 양은 엄청나다
- 페이스북 메신저나 와츠앱은 매일 600억 개의 메시지를 처리
- 빈번하게 사용되는 것은 주로 최근에 주고받은 메시지
- 대부분은 오래된 메시지를 들여다보지 않는다.
- 사용자는 대체로 최근에 주고받은 메시지 데이터만 보게 되는 것이 사실이나, 검색 기능을 이용하거나, 특정 사용자가 언급된 메시지를 보거나, 특정 메시지로 점프 하거나 하여 무작위적인 데이터 접근을 하게 되는 일도 있음
- 1:1 채팅 앱의 경우 읽기:쓰기 비율이 대략 1:1 정도
- 이런 기능을 지원하기 위해서는 키-값 저장소를 추천
- 키-값 저장소는 수평적 규모 확장이 쉬움
- 데이터 접근 지연시간이 낮다
- 관계형 데이터베이스는 데이터 가운데 롱 테일에 해당하는 부분을 잘 처리하지 못하는 경향이 있음
- 관계형 데이터베이스는 인덱스가 커지면 데이터에 대한 무작위적 접근을 처리하는 비용이 늘어난다
- 많은 안정적인 채팅 시스템이 키-값 저장소를 채택하고 있음
- 페이스북 메시저(HBase), 디스코드(Cassandra) 등
- 채팅 이력 데이터의 양은 엄청나다
- 사용자 프로파일, 설정, 친구 목록 처럼 일반적인 데이터
데이터 모델
- 1:1 채팅을 위한 메시지 테이블
- message_id: bigint
- 기본 키, 메시지 순서를 쉽게 정할 수 있도록 하는 역활
- message_from: bigint
- message_to: bigint
- content: text
- create_at: timestamp
- message_id: bigint
- 그룹 채팅을 위한 메시지 테이블
- channel_id: bigint
- 파티션 키로도 사용할 수 있음
- message_id: bigint
- message_from: bigint
- message_to: bigint
- content: text
- create_at: timestamp
- channel_id, message_id는 복합키를 기본키로 사용
- channel_id: bigint
메시지 ID
- message_id는 고유해야 한다
- ID 값은 정렬 가능해야 하며 시간 순서와 일치해야 한다.
- 새로운 ID는 이전 ID 값보다 큰 값이어야 한다.
- RDBMS의 경우 auto_increment가 대안, NoSQL은 해당 기능을 제공하지 않음
- 스노플레이크 같은 전역적 64-bit 순서 번호 생성기를 이용하는 것
- 지역적 순서 번호 생성기를 이용하는 것
- 여기서 지역적이라 함은 ID의 유얼성은 같은 그룹 안에서만 보증하면 충분하다는 것
- 메시지 사이의 순서는 같은 채널 혹은 같은 1:1 채팅 세션 안에서만 유지되면 충분하기 때문
- 전역적 ID 생성기에 비해 구현하기 쉬운 접근법
3단계 상세 설계
서비스 탐색
- 서비스 탐색 기능의 주된 역활은 클라이언트에게 가장 적합한 채팅 서버를 추천하는 것
- 추천하는 기준으로는 클라이언트의 위치, 서버의 용량 등이 있다.
- 널리 쓰이는 오픈 소스 솔루션으로는 아파치 주키퍼 같은 것이 있다.
- 사용 가능한 모든 채팅서버를 여기에 등록시켜 두고, 클라이언트가 접속을 시도하면 사전에 정한 기준에 따라 최적의 채팅 서버를 골라 주면 된다.
- [그림 12-11]은 주키퍼로 구현한 서비스 탐색 기능이 어떻게 동작하는지 보여준다.
- 사용자가 A 시스템에 로그인
- 로드밸런서가 로그인 요청을 API 서버들 가운데 하나로 보낸다.
- API 서버가 사용자 인증을 처리하고 나면 서비스 탐색 기능이 동작하여 해당 사용자를 서비스할 최적의 채팅 서버를 찾는다.
- 사용자 A는 채팅 서버 2와 웹소켓 연결을 맺는다.
메시지 흐름
1:1 채팅 메시지 처리 흐름
- [그림 12-12]는 1:1 채팅에서 사용자 A가 B에게 보낸 메시지가 어떤 경로로 처리 되는지 보여줌
- 사용자 A가 채팅 서버 1로 메시지 전송
- 채팅 서버 1은 ID 생성기를 사용해 해당 메시지의 ID 결정
- 채팅 서버 1은 해당 메시지를 메시지 동기화 큐로 전송
- 메시지가 키-값 저장소에 보관됨
- 사용자 B가 접속 중인 경우 메시지는 사용자 B가 접속중인 채팅 서버로 전송됨
- 사용자 B가 접속 중이 아닌 경우 푸시 알림 메시지를 푸시 알림 서버로 보냄
- 채팅 서버 2는 메시지를 사용자 B에게 전송, 사용자 B와 채팅 서버 2 사이에는 웹소켓 연결이 있는 상태이므로 그것을 이용
728x90
'가상 면접 사례로 배우는 대규모 시스템 설계 기초' 카테고리의 다른 글
유튜브 설계 (2) | 2023.11.09 |
---|---|
검색어 자동완성 시스템 (0) | 2023.11.09 |
뉴스 피드 시스템 설계 (0) | 2023.11.09 |
알림 시스템 설계 (1) | 2023.11.09 |
알림 시스템 설계 (0) | 2023.11.09 |