채팅 아키텍처 설계하기 v1

hyng·2023년 1월 8일
5

smilegate-winter-dev-camp

목록 보기
5/15

이번에 진행하는 프로젝트에서 채팅, 신고 도메인을 맡았고 캠프장님께 아키텍처 리뷰를 받기 전에 간략하게 설계한 아키텍처를 기록한다.

구현해야 할 기능

우리 팀의 프로젝트 목표는 '대용량 시스템 설계 능력' 함양이다. 그래서 다양한 기능을 구현하는 것보다는 단순한 기능을 고도화 시키는 것으로 가닥을 잡았다. 실제 현업에서는 구현하기 전에 요구사항 명세(TPS, 동시 접속자 수.. 등등)가 나오지만 아직 우리가 이런 부분을 생각하기에는 부족하다는 의견이 많아서 처음엔 단순하게 아키텍처를 설계하고 우리 프로젝트와 유사한 서비스의 트래픽의 일정 퍼센트를 목표로 잡고 성능 테스트를 통해 서버를 점점 개선해가는 방향으로 나아가기로 했다. 그래서 책, 영상, 글 등을 참고하여 개략적으로 아키텍처를 설계하였다. 이번 주에 예정된 캠프장님의 리뷰를 통해 일정 부분 달라질 것 같다. 달라진 부분은 버전을 붙여 글로 남겨둘 예정이다!
간략한 요구사항은 아래와 같다.

  • 채팅
  1. 라이브를 보는 사용자는 라이브를 보면서 채팅 기능을 사용할 수 있다.
    2. 라이브를 보는 사용자는 하트를 눌러 방송자를 응원할 수 있다.
  2. 채팅 내용은 라이브 시간 동안에만 유지되면 된다.
  3. 채팅 내용은 수정 또는 삭제할 수 없다.
  • 신고
  1. 라이브를 보는 사용자들은 불쾌한 채팅을 남기는 사용자를 신고할 수 있고 10번 이상 신고된 사용자는 해당 라이브에서 채팅이 금지된다.
  2. 신고 데이터는 영구적으로 유지되어야 한다.

채팅

  1. 클라이언트 <-> 서버 통신 프로토콜
    클라이언트와 서버 통신 프로토콜은 websocket으로 결정하였다. 그리고 이외에 SockJS(브라우저에서 웹소켓 지원하지 않을 경우 http로 재시도) 사용하기로 결정하였다.
    http 폴링 혹은 롱 폴링 방식은 실시간성을 보장하지 못하고 서버의 리소스 낭비가 있다는 점에서 웹소켓으로 결정하였다.
    자세한 내용은 가상 면접 사례로 배우는 대규모 시스템 설계 기초를 참고하였다.

아래는 책의 내용을 일부 정리한 부분

  • http

    • 오랫동안 사용되어왔고 검증된 프로토콜

    • keep-alive 헤더를 이용하여 지속연결 할 수 있고 그외 http 의 여러 장점(캐시 헤더, rest 아키텍처.. 등등) 들 활용 가능

    • 하지만 서버에서 클라이언트로 데이터 전송 어떻게?

      • 폴링

        • 클라이언트가 주기적으로 서버에게 새 메시지가 있느냐고 물어보는 방법

      • 롱폴링
        - 클라이언트는 새메시지가 반환되거나 타임아웃 될 때까지 연결을 유지
        - 클라이언트는 새 메시지를 받으면 기존 연결을 종료하고 서버에 새로운 요청을 보내어 모든 절차를 다시 시작한다
        - 로드밸런싱을 위해 라운드 로빈 알고리즘을 사용하는 경우 메시지를 받은 서버는 해당 메시지를 수신할 클리언트와 롱 폴링 연결을 가지고 있지 않은 서버 일수도 있다

        → 실시간성을 제대로 보장하지 못하고 불필요한 오버헤드

  • websocket ✅

    • 클라이언트 ↔ 서버는 계속 연결을 유지하기 때문에 실시간성이 보장됨
    • 양방향 메시지 전송 가능
  1. pub/sub
    메시지 전송자와 수신자들 사이에 느슨한 결합을 위해 메시징 시스템을 사용한다.
    pub/sub 패턴을 사용함으로써 메시지 전송자와 수신자는 서로를 모르는 상태에서 메시지를 주고받을 수 있고 독립적으로 확장 및 수정 가능하다. 참고
    pub/sub 구조, 메시지 포맷(SUBSCRIBE, SEND, UNSUBSCRIBE..) 고정, 헤더 사용 등을 이유로 STOMP 프로토콜을 사용하기로 결정하였다. 이로써 websocket + sockjs + stomp를 사용하는 구조이다.

  2. 여러 웹소켓 서버 사이 메시지 전달
    웹소켓은 한번 연결을 맺으면 동일한 서버에 계속 연결을 유지하는 형태이다. 그렇기 때문에 많은 사용자에게 서비스를 제공하기 위해서는 여러 웹소켓 서버가 필요하게 되고 동일한 채널에 속한 사용자 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로 결정하였는데 이 부분은 캠프장님과의 리뷰 시간을 통해 수정될 거 같기도 하다.

  1. 채팅 데이터 저장
    우리 프로젝트에서는 채팅 데이터를 라이브 시간 동안에만 저장한다. 그래서 현재는 접근 속도가 빠른 redis 캐시를 생각 중이다. 그런데 한대의 서버로 넘치는 채팅을 감당하지 못한다면? 레디스 클러스터를 구축하는 방법도 있지만 서비스가 성장하면 근본적인 해결책이 필요하지 않을까?
    접근속도가 빠른 nosql로 하는건 어떨까? 이렇게 되면 대용량 데이터 처리 문제를 해결하기 위해 일정 시간마다 데이터베이스로 채팅 데이터를 옮기고 읽기 작업시 데이터베이스로 요청해서 처리한다고 해도 접근속도가 빠를 것임. (접근속도가 빠르고, 삽입, 읽기 작업이 빠른 데이터베이스를 선택해야함)

line live 채팅 참고

채팅의 임시데이터는 레디스에 저장하고 영속 데이터는 정규화해서 배치를 통해 mysql에 저장

배민 쇼핑 라이브 채팅 참고
라이브가 끝나면 휘발되는 데이터는 레디스에 저장한다.

이 부분도 캠프장님과의 리뷰 시간을 통해 수정될 거 같다!

신고

  1. 10번 이상 신고당한 사용자는 채팅 사용이 금지된다.
    신고 데이터는 안정성을 보장하는 관계형 데이터베이스인 mysql을 사용하는 것을 고민했다. 그런데 채팅 메시지를 보내기 전에 신고 데이터를 조회해야 할 필요가 있기 때문에 mysql 접근으로 인해 채팅 메시지 전달 속도가 떨어지지 않을까 하는 걱정이 있었다. 그래서 일단 redis 캐시를 이용해 데이터를 저장하고 일정 시간마다 배치 처리를 통해 저장된 데이터를 mysql로 옮기는 것을 생각했다.
    이부분도 고민인게 이번 프로젝트에서 msa 를 적용하는게 요구사항중 하나인데 두개의 서버에서 하나의 캐시에 접근해도 괜찮은걸까..? 이부분도 리뷰시간을 통해 여쭤볼 것 같다.

도식화

아키텍처

처리순서

현재 고민 중인 내용 정리

  1. 메시지 브로커로서 redis를 사용할 때 대용량 데이터 처리에서 문제가 발생하진 않을까?
  2. 레디스 클러스터 보다는 일정시간마다 데이터베이스에 데이터를 저장하는 편이 더 낫지 않을까?
    2-1. 그러면 nosql로 바로 접근하는게 낫지 않을까?
  3. 신고 서버와 채팅 서버가 하나의 '신고캐시'를 공유하는데 msa 관점에서 괜찮은걸까? 두 서버가 동일한 캐시 서버를 공유할 수밖에 없다면 사실 신고와 채팅을 분리할 필요가 없는것 아닌가?
profile
공부하고 알게 된 내용을 기록하는 블로그

0개의 댓글