Chatting
1 Chatting
- 채팅 서비스는 크게 세 가지 핵심 부분으로 구성됩니다
- HTTP API Server
- 채팅 서버(접속 상태 서버)
- 푸시 알림 서버
1.1 HTTP API Server
- API 서버는 로그인, 등록, 사용자 프로필 표시와 같은 전통적인 요청/응답 작업을 관리하는 무상태 서비스로 운영됩니다.
- 로드 밸런서 뒤에 위치하며, 사용자 상태 정보를 유지하지 않고 사용자 요청을 효율적으로 처리합니다.
1.2 채팅 서버(접속 상태 서버)
- API 서버와 달리 채팅 서버는 상태를 유지하며, 각 클라이언트가 지속적인 네트워크 연결을 유지합니다.
- 이 설계는 클라이언트가 동일한 서버에 지속적으로 연결되어 있도록 하여 메시지 일관성과 실시간 통신 기능을 향상시킵니다.
- 서버는 로드를 분산하고 연결 라우팅을 효과적으로 관리하기 위해 서비스 발견 메커니즘(예: Apache Zookeeper)을 활용합니다.
1.3 푸시 알림 서버
- 푸시 알림 서버는 사용자가 오프라인이거나 비활성 상태일 때 메시지를 전달하는 데 있어 중요합니다.
- 이를 통해 사용자가 새 메시지와 참여에 대해 계속 정보를 받을 수 있도록 하여 전반적인 사용자 경험을 향상시킵니다.
2 저장소
- 채팅 서비스의 데이터 저장소는 사용자 관련 데이터를 위한 관계형 데이터베이스와 채팅 내역 및 메시지를 처리하기 위한 NoSQL 데이터베이스의 조합입니다.
- 이 접근법은 데이터 무결성, 확장성 및 빠른 접근을 위한 최근 및 자주 사용되는 정보의 균형을 맞춥니다.
- 사용자 프로파일, 설정, 친구 목록과 같은 데이터는 안정성을 보장하는 관계형 데이터베이스에 저장
- 채팅 시스템 고유의 데이터 즉 채팅 이력은 어디에 저장할 것인가?
- 채팅 이력 데이터는 양은 엄청나다.
- 이 데이터 중 빈번하게 사용된ㄴ 것은 최근 메시지이다.
- 검색 기능을 사용해 특정 사용자가 언급된 메시지를 보거나 특정 메시지로 점프하는 무작위적인 데이터 접근을 하기도 한다.
- 1:1 채팅의 경우 읽기 쓰기 비율이 대략 1:1이다
3 데이터 모델
- 채팅 시스템의 데이터 모델은 메시지의 고유성과 적절한 시간 순서를 보장하면서 일대다 통신 시나리오를 효과적으로 처리할 수 있어야 합니다
3.1 메시지 ID
- 메시지 ID가 충족해야할 조건
- ID는 고유해야 한다.
- 정렬 가능해야 하며 시간 순서와 일치해야 한다.
- 즉 새로운 ID는 이전 ID보다 큰 값이어야 한다.
- [[Unique-Id-Generator]] 참고
4 흐름
4.1 서비스 탐색 과정
- 서비스 탐색이란 클이언트에게 가장 적합한 채팅 서버를 추천해주는 것이다.
- 주로 클라이언트의 위치, 서버의 용량을 기준으로 탐색한다.
- 오픈 소스 솔루션으로 아파치 주키퍼가 있으며 사용 가능한 채팅 서버를 주키퍼에 등록하고 클라이언트가 접속을 시도하면 사전에 정한 기준에 따라 최적의 채팅 서버를 골라 주면 된다.
서비스 탐색 과정
- 사용자 A가 시스템에 로그인을 시도한다.
- 로드밸런서가 로그인 요청을 API 서버 중 하나로 보낸다.
- API 서버가 로그인을 처리하고 서비스 탐색 기능을 동작해 사용자가 사용할 최적의 채팅 서버를 찾아 사용자에게 반환한다.
- 사용자 A는 반환 받은 채팅 서버와 웹소켓 연결을 맺는다.
4.2 메시지 처리 과정
- 사용자가 채팅 서버 1에 메시지 전송
- 채팅 서버 1은 ID 생성기를 통해 해당 메시지의 ID를 결정
- 채팅 서버 1은 해당 메시지를 메시지 동기화 큐로 전송
- 메시지 동기화 큐에서 키-값 저장소로 메시지 보관됨
- 메시지 동기화 큐에서 메시지는
- 사용자 B가 접속중이면 채팅 서버 2로 전송됨
- 사용자 B가 접속중이 아니면 메지시를 푸시 알림 서버로 보냄
- 채팅 서버 2는 메시지를 사용자 B에게 전송한다.
- 사용자 B와 채팅 서버 2 사이에는 웹소켓이 연결되어 있는 상태
4.3 여러 단말 사이 메시지 동기화 과정
- 사용자 A의 전화기와 랩톱 두 대의 단말을 사용하고 있다.
- 각 단말은 cur_max_message_id라는 변수를 유지한다.
- 해당 단말에서 관측된 최신 메시지 ID를 담고있다.
- 단말마다 별도로 유지 관리되는 값이다.
- 아래의 두 조건을 만족하면 새로운 메시지로 간주한다.
- 수신자 ID가 현재 로그인한 사용자 ID와 같다.
- 메시지의 ID가cur_max_message_id보다 크다.
4.4 접속상태 표시 과정
- 사용자가 API 서버를 통해 로그인하고 클라이언트와 접속상태 서버와 웹소켓 연결이 맺어지면 접속상태 서버는 A의 상태와 lastActiveAt 타임 스탬프 값을 키-값 저장소에 보관한다.
- 이 절차가 끝나면 해당 사용자는 접속 중인 것으로 표시된다.
- 사용자가 API 서버를 통해 로그아웃하면 API 서버는 접속상태 서버에 요청을 보내 키-값 저장소에 보관된 사용자 상태가 online에서 offline으로 바뀌게 된다.
접속 장애
- 인터넷을 통한 연결은 불안정하다.
- 사용자의 인터넷 연결이 끊 어지면 클라이언트와 서버 사이에 맺어진 웹소켓 같은 지속성 연결이 끊어진다.
- 이런 장애의 대응 방법은 사용자를 오프라인 상태로 표시하고 연결이 복구되면 온라인 상태로 변경하는 것이다.
- 하지만 이런 방법은 문제가 많다.
- 짧은 시간동안 인터넷 연결이 끊어졌다 복구되는 일은 흔하다.
- 따라서 heartbeat 검사를 통해 이 문제를 해결한다.
- 주기적으로 클라이언트가 heartbeat event를 접속 상태 서버로 보내고 마지막 이벤트를 받은 지 X초 이내에 또 다른 heartbeat event를 보내면 해당 사용자의 접속상태를 계속 온라인으로 유지하는 것이다.