이번에 진행하는 프로젝트에서 채팅, 신고 도메인을 맡았고 캠프장님께 아키텍처 리뷰를 받기 전에 간략하게 설계한 아키텍처를 기록한다.
우리 팀의 프로젝트 목표는 '대용량 시스템 설계 능력' 함양이다. 그래서 다양한 기능을 구현하는 것보다는 단순한 기능을 고도화 시키는 것으로 가닥을 잡았다. 실제 현업에서는 구현하기 전에 요구사항 명세(TPS, 동시 접속자 수.. 등등)가 나오지만 아직 우리가 이런 부분을 생각하기에는 부족하다는 의견이 많아서 처음엔 단순하게 아키텍처를 설계하고 우리 프로젝트와 유사한 서비스의 트래픽의 일정 퍼센트를 목표로 잡고 성능 테스트를 통해 서버를 점점 개선해가는 방향으로 나아가기로 했다. 그래서 책, 영상, 글 등을 참고하여 개략적으로 아키텍처를 설계하였다. 이번 주에 예정된 캠프장님의 리뷰를 통해 일정 부분 달라질 것 같다. 달라진 부분은 버전을 붙여 글로 남겨둘 예정이다!
간략한 요구사항은 아래와 같다.
아래는 책의 내용을 일부 정리한 부분
http
오랫동안 사용되어왔고 검증된 프로토콜
keep-alive 헤더를 이용하여 지속연결 할 수 있고 그외 http 의 여러 장점(캐시 헤더, rest 아키텍처.. 등등) 들 활용 가능
하지만 서버에서 클라이언트로 데이터 전송 어떻게?
폴링
클라이언트가 주기적으로 서버에게 새 메시지가 있느냐고 물어보는 방법
롱폴링
- 클라이언트는 새메시지가 반환되거나 타임아웃 될 때까지 연결을 유지
- 클라이언트는 새 메시지를 받으면 기존 연결을 종료하고 서버에 새로운 요청을 보내어 모든 절차를 다시 시작한다
- 로드밸런싱을 위해 라운드 로빈 알고리즘을 사용하는 경우 메시지를 받은 서버는 해당 메시지를 수신할 클리언트와 롱 폴링 연결을 가지고 있지 않은 서버 일수도 있다
→ 실시간성을 제대로 보장하지 못하고 불필요한 오버헤드
websocket ✅
pub/sub
메시지 전송자와 수신자들 사이에 느슨한 결합을 위해 메시징 시스템을 사용한다.
pub/sub 패턴을 사용함으로써 메시지 전송자와 수신자는 서로를 모르는 상태에서 메시지를 주고받을 수 있고 독립적으로 확장 및 수정 가능하다. 참고
pub/sub 구조, 메시지 포맷(SUBSCRIBE, SEND, UNSUBSCRIBE..) 고정, 헤더 사용 등을 이유로 STOMP 프로토콜을 사용하기로 결정하였다. 이로써 websocket + sockjs + stomp를 사용하는 구조이다.
여러 웹소켓 서버 사이 메시지 전달
웹소켓은 한번 연결을 맺으면 동일한 서버에 계속 연결을 유지하는 형태이다. 그렇기 때문에 많은 사용자에게 서비스를 제공하기 위해서는 여러 웹소켓 서버가 필요하게 되고 동일한 채널에 속한 사용자 1은 웹소켓 서버 1에 연결, 사용자 2는 웹소켓 서버 2에 연결된 형태가 된다. 이때 사용자 1이 메시지를 보내더라도 사용자 2에게 전달되어야 한다. 그래서 웹소켓 서버들 사이에 메시지 브로커가 필요하다.
메시지 브로커를 위한 기술로는 크게 redis와 kafka를 고려했다.
두 기술에 대해 알아본 결과 실시간성과 대용량 데이터 사이 트레이드 오프라는 결론을 내렸다. (이외에도 여러 요인이 많겠지만 크게 두 가지만 고려했다.)
다른 채팅 서비스들의 기술 스택 조사를 해보니 여러 서비스들에서 redis pub/sub을 사용해서 서비스를 하고 있는 것을 확인했다.
(1/11 채팅 서버 설계 관련하여 redis / kafka 비교하는 영상을 찾았다. https://www.youtube.com/watch?v=73Utd7nDYDs)
동영상 내용 요약
- kafka는 메시지 전달 속도가 redis에 비해 느림
- kafka는 디스크에 데이터를 쓰고 읽는 반면 redis는 메모리에 데이터를 쓰고 읽기 때문.
- kafka는 Public, subscribe 하기 위한 topic 생성이 필요한데, 이건 매우 무거운 작업
- 반면에 redis는 채널 생성이 필요없음.
- kafka는 디스크를 사용하기 때문에 storage requirement가 존재
- kafka는 topic 삭제시 삭제가 제대로 동작하지 않는다면 kafka 서버를 멈추고 topic를 수동으로 삭제해야함
대용량 데이터 측면에서는 큰 문제는 없지 않을까 하는 마음에 일단 redis로 결정하였는데 이 부분은 캠프장님과의 리뷰 시간을 통해 수정될 거 같기도 하다.
line live 채팅 참고
채팅의 임시데이터는 레디스에 저장하고 영속 데이터는 정규화해서 배치를 통해 mysql에 저장
배민 쇼핑 라이브 채팅 참고
라이브가 끝나면 휘발되는 데이터는 레디스에 저장한다.
이 부분도 캠프장님과의 리뷰 시간을 통해 수정될 거 같다!