채팅 아키텍처 설계하기 v2

hyng·2023년 1월 12일
2

smilegate-winter-dev-camp

목록 보기
6/15

이전 버전에서 새롭게 수정된 부분과 최종적으로 완성된 아키텍처를 기록한다. (개발하면서 개선해 갈 예정)
개발하면서 변경될 수도 있지만 일단 현재 결정된 아키텍처📍

고민한 부분

  1. 메시지 브로커로서 redis를 사용할 때 대용량 데이터 처리에서 문제가 발생하진 않을까?
    redis cluster 구축해보자

  2. 레디스 클러스터 보다는 일정시간마다 데이터베이스에 데이터를 저장하는 편이 더 낫지 않을까?
    2-1. 그러면 nosql로 바로 접근하는게 낫지 않을까?
    이 부분은 구글링을 하면서 정확히 어떤 부분을 고민했던것인지 더 정확해졌다. 정확하게 우려했던 부분은 "레디스를 메인 스토리지로 이용할 때 메모리 용량이 부족하지 않을까?"였다. 관련 내용을 찾아봤고 찾아본 결과 "서비스가 성장하고 저장해야 하는 데이터양이 증가하면서 문제가 될 수 있다"로 결론을 내렸다.

    참고한 링크
    https://charsyam.wordpress.com/2012/04/29/%EB%B0%9C-%EB%B2%88%EC%97%AD-line-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%ED%95%9C%EB%8B%AC%EC%97%90-%EC%88%98%EC%8B%AD%EC%96%B5-%EA%B1%B4%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-redis%EC%99%80/
    https://engineering.linecorp.com/ko/blog/LINE-integrated-notification-center-from-redis-to-mongodb

그래서 mongodb에 데이터를 저장하는것으로 결정했다.
1.유연한 스키마(추후 데이터 추가 및 변경 될 수 있음)
2.수평적 확장이 쉽다
3.데이터 접근 지연시간이 낮음
4.TTL 인덱스 지원
채팅 데이터는 라이브 방송이 끝나면 삭제되어야한다.
5.read/write 성능 뛰어남(write에 최적화된 cassandra 제외)
6.러닝커프가 너무 높지 않은 수준이어야함(hbase 제외)
이후 성능 테스를 통해 캐시를 도입해서 응답 속도를 높이는 것도 좋을 듯하다.

  1. 신고 서버와 채팅 서버가 하나의 '신고 캐시'를 공유하는데 msa 관점에서 괜찮은 걸까? 두 서버가 동일한 캐시 서버를 공유할 수밖에 없다면 사실 신고와 채팅을 분리할 필요가 없는 것 아닌가?
    이 부분은 슬랙에 질문을 올렸고 기술 멘토 분이 피드백을 해주셨다.

    질문
    저희 팀 프로젝트에서는 채팅에서 사용자들끼리 서로를 신고할 수 있고 신고횟수가 일정 수 이상 되면 해당 사용자의 채팅을 막고있습니다. 이때 신고 서버에서는 http api를 통해 사용자 신고를 받고 있고 요청을 받아서 이 데이터를 캐시 서버에 저장하면 채팅 서버에서 이 캐시 데이터를 확인해서 사용자의 채팅을 차단하는식으로 설계해두었는데 msa관점에서 이 두 서버가 동일한 캐시를 공유해도 되는것인지? 공유할 수밖에 없다면 굳이 두 서버를 분리할 필요가 없는게 아닌지? 의문이 들었습니다. 두 서버를 분리하는게 맞을까요?

요약하자면,

  • 아키텍처 관점에서 신고를 분리해야 하는 이유를 생각해 보면 좋다.
  • 신고 기능에 대한 요구사항 변경이 잦아서 앞으로 변경/배포가 자주 일어날 것 같다든지, 신고 기능에 트래픽이 몰릴 것 같아서 별도의 scale-out 이 필요하다든지
  • 만약 분리가 필요하다면 서로 다른 마이크로 서비스들이 데이터 저장소를 공유하는 것은 결합도가 높아지기 때문에 권장되지 않는다.
    채팅 서버에서 신고 서버로 해당 사용자가 차단되어 있는 사람인지 API를 호출하여 확인하는 방식으로 구현하는 것이 데이터를 공유하는 것보다 나을 수 있다.

피드백 주신 내용을 바탕으로 신고를 분리할 필요가 있나 생각해 봤다.
결론적으로 먼저 "분리하는 것으로 결정" 하였는데 이유는
1. 우리 팀 프로젝트의 목표는 '대용량 시스템 설계 능력 함양"이다.
채팅 서버는 많은 트래픽이 발생하는 부분이기 때문에 오롯이 채팅 기능만 담당하는 게 좋을 것 같다고 생각했다.
2. 신고 도메인은 방송자가 시청자를 신고 및 강퇴하는 경우 등등 추후 다른 요구사항이 추가될 수 있다.

채팅

변경된 부분은 채팅 메시지 저장 부분이다. 기존에는 레디스를 메인 스토리지로 이용했던 부분이 nosql에 저장하는 것으로 바뀌었다.
그리고 채팅 서버들 사이에 차단 사용자 캐시를 공유한다.
차단 사용자 캐시는 신고 서버에서 kafka에 이벤트를 발행하면
캐시 서버들 중 하나가 이벤트를 받아 차단 캐시를 업데이트하고 클라이언트로 차단된 사용자 정보를 전송한다.(웹소켓 통해)

그러면 각 채팅 서버로 채팅 메시지 전송 요청이 들어오면 채팅 서버는 차단 사용자 캐시를 확인하고 채팅이 차단된 사용자라면 채팅 메시지를 보내지 않는다.

이때 차단된 사용자의 경우 이 사용자가 이전에 보낸 채팅 메시지들을 "차단된 사용자입니다" 이런 식으로 가려야 하는데 이 사용자가 이전에 엄청나게 많은 채팅 메시지를 전송했다면 많은 업데이트 연산이 발생할 수 있기 때문에 별도의 배치 서버에서 처리하는 것으로 결정했다.

채팅방 정보를 저장할 필요가 있는데 DB에 저장이 필요하지 않고 방송이 끝나면 휘발되는 데이터는 레디스에 저장하는 것으로 결정했다.

신고

신고 서버와 채팅 서버를 분리하는 것으로 결정했기 때문에 신고 서버에서는
신고 요청이 들어왔을 때 mysql에 데이터를 저장하고 로컬 캐시를 통해 "어떤 사용자가 몇 번 신고되었는지"를 저장한다.
그리고 만약 신고 횟수가 10번이 되면 kafka에 이벤트를 발행한다.
데이터베이스는 안정성을 보장하며 오픈소스이고 레퍼런스가 많은 mysql로 결정했다.

도식화

  • 아직 라이브 방송 서버와 어떻게 통신할지를 결정하지 못해 이 부분은 아키텍처에 포함시키지 않았다.

고민 중인 부분

  • 라이브 방송이 시작, 종료되었을 때 채팅방 정보를 업데이트할 것인데 라이브 방송 서버와 어떻게 통신할지? (어떤 방법, 어떤 기술)

    2023.01.18 추가 🔽

  • 그냥 http로 통신하는 방법과 서버 사이에 메시지큐를 두는 방법 고려했음
  • 현재까지 http로 통신하는 방법으로 결정하였으나 팀원분이 비동기 통신에 대해 더 알아보고 결정하기로 하였다.
  • 메시지 큐가 제공해 주는 장점을 고려하면서 http로 충분히 상쇄 시킬 수 있는 부분인지 파악했다.
  • 메시지 큐가 제공해주는 장점
    • 느슨한 결합
      • 장애가 전파되지 않도록 해준다. → circuit breaker 활용하면 http에서도 가능할듯
      • 응답을 기다리지 않고 비동기 통신 →@async 사용, 부족하면 timeout 시간을 빠르게(비동기 통신이 맞나..? 알아봐야함)
      • 데이터 보관 → 수신자 서버에 장애가 발생하더라도 재시작 후 메시지큐에 있는 요청을 이어서 처리할 수 있음
        • 라이브 방송이 시작하면 채팅서버로 요청을 보내는부분이라 30분에 한번 정도 발생할것으로 예상이 되기 때문에 저장되어 있지 않은 채팅방 id로 메시지 전송 요청이 오면 방송 서버에 요청해서 처리 하는식으로 해도 될듯함
      • publisher, subscriber 서로를 몰라도 됨 <- 🤔현재로서는 이부분이 메시지큐를 사용했을때 장점일것 같음
  • 메시지 큐 사용시 고려해야 하는 문제점
    • 수신한 메시지 처리가 실패했을 때 어떻게 할 것인가?
    • 메시지 처리 순서를 어떻게 정할 것인가?
      • 방송 서버에서 메시지를 발행하면 알림서버와 채팅서버에서 받아서 처리할 예정, 알림 서버와 채팅 서버의 메시지 처리 순서는 중요하지 않기 때문에 해당 문제는 고려하지 않아도 될 듯함.
    • 학습 시간 고려
      • 대용량 처리가 필요하지 않기 때문에 rabbitmq를 사용한다고 하더라도 http 요청을 보내는 것으로도 충분한 부분을 오버 엔지니어링 한 것일 수 있음 + 사용해 본 팀원이 없기 때문에 학습시간도 고려해야 함
  • 참고 자료
    스마일게이트 강의
    https://rapidapi.com/guides/difference-kafka-rest
    https://www.interviewbit.com/blog/rabbitmq-vs-kafka/
    https://velog.io/@cho876/카프카kafka-vs-RabbitMQ

    2023.01.27 추가 🔽

    미디어 서버에서 채팅 서버로 rest-api 호출을 할 때 적어도 한번 전달되도록 보장할 수 있는지를 생각해 보게 되었음

    1. 채팅 서버가 요청을 받지 못한다면?
      -> 메시지 전송 요청이 왔을 때 존재하지 않는 채팅방 id라면 미디어 서버에 요청을 보내서 확인하기로 했음
      -> 이 요청을 얼마나 기다릴지 설정이 필요할듯함. 서킷 브레이커 고려
    2. 미디어 서버가 채팅방 서버로 요청을 보내지 못한다면?
      -> 라이브 서비스도 동작하지 못하는 상태가 되기 때문에 채팅방도 생성되면 안 됨
    3. 미디어 서버가 채팅방 서버의 요청을 받지 못한다면?
      -> 라이브 서비스도 동작하기 못하는 상태가 되기 때문에 채팅방도 생성되면 안 됨

    추가로 미디어 서버에서 채팅방 서버로 제때 api 호출을 하지 못해 메시지 전송 요청이 왔을 때 미디어 서버로 api 호출을 해서 채팅방 생성 여부를 확인하게 되는데 이때 시간이 오래 걸리지 않을까?
    -> 첫 사용자의 메시지 전송 요청에만 약간 시간이 걸리고 이후부터는 정상적으로 요청을 처리하기 때문에 괜찮을듯.
    참고자료: https://youtu.be/uk5fRLUsBfk

학습이 필요한 부분

  • 채팅 서버 로드밸런싱
  • 레디스 클러스터 (redis pub/sub)
profile
공부하고 알게 된 내용을 기록하는 블로그

0개의 댓글