가상면접 사례로 배우는 대규모 시스템 설계 기초 11장~12장 정리

정훈희·2023년 3월 30일
0
post-thumbnail

[11장] 뉴스 피드 시스템 설계

뉴스 피드 API

  • 피드 발행 API

    image

    • 포스팅 저장 서비스: 새 포스팅을 DB와 캐시에 저장
    • 포스팅 전송 서비스: 새 포스팅을 친구의 뉴스 피드에 push. 뉴스 피드 데이터는 캐시에 보관하여 빠르게 읽을 수 있도록 함
    • 알림 서비스: 친구들에게 새 포스팅이 올라왔음을 알리거나 푸시 알림을 보내는 역할을 담당
  • 피드 읽기 API: 뉴스 피드 서비스가 캐시에서 뉴스 피드 ID를 가져와서 랜더링

피드 발행 상세 설계

image

  • 웹 서버에서는 인증 및 처리율 제한 등의 기능 수행
  • 포스팅 전송(팬아웃)서비스: 팬아웃은 쓰기 시점에 팬아웃하는 푸시 모델과 읽기 시점에 팬아웃 하는 풀 모델이 있다.
    • 팬아웃: 어떤 사용자의 새 포스팅을 그 사용자의 친구들에게 전달하는 과정

    • push 모델: 포스팅 기록시 캐시에 바로 기록

      • 장점: 뉴스 피드가 실시간으로 갱신, 읽기 시간 감소
      • 단점: 친구가 많은 사용자는 모든 사용자의 뉴스 피드를 갱신하는데 많은 시간이 소요되는 문제 발생(hotkey 문제)
    • pull 모델: 피드를 읽는 시점에서 캐시에 기록
      - 장점: 비활성화 된 사용자, 서비스에 로그인하지 않는 사용자의 경우엔 해당 모델이 유리, 데이터를 친구 각각에 push 하는 작업 필요 X → hotkey 문제 발생 X
      - 단점: 읽기 시간 증가

      두 모델을 적절히 결합 → 대부분의 사용자에 대해서는 push 모델 사용(읽기 시간 감소는 매우 중요), 친구가 매우 많은 사용자는 pull 모델 사용, 안정 해시를 통해 요청과 데이터를 고르게 분산하여 hotkey 문제 감소

      팬아웃 서비스 동작 과정

    1. 그래프 DB에서 친구 ID 목록 가져옴
    2. 사용자 정보 캐시에서 친구들의 정보를 가져와서 설정에 따라 필터링
    3. 친구 목록과 새 스토리의 ID를 MQ에 넣는다
    4. 팬아웃 작업 서버가 메시지 큐에서 데이터를 꺼내어 뉴스 피드 데이터를 뉴스 피드 캐시에 넣음
      • 뉴스 피드 캐시는 <포스팅 ID, 사용자 ID> 순서쌍을 보관하는 매핑 테이블
      • ID만 보관하는 이유는 메모리 요구량이 지나치게 증가할 수 있기 때문
      • 또한 메모리 크기를 적정 수준으로 유지하기 위해서, 캐시 크기에 제한을 둠 → 사용자가 뉴스 피드에 올라온 수천 개의 스토리를 전부 볼 일은 매우 낮음 → 캐시 미스가 일어날 확률은 낮음

피드 읽기 상세 설계

image

  • 이미지나 비디오 같은 미디어 콘텐츠는 빠르게 읽을 수 있도록 CDN에 저장
  • 뉴스 피드 서비스는 뉴스 피드 캐시에서 포스팅 ID 목록을 가져옴
  • 뉴스 피드에 표시할 사용자 이름, 콘텐츠 등을 사용자 캐시와 포스팅 캐시에서 가져와 완전한 뉴스 피드를 만듦
  • 생성된 뉴스 피드를 클라이언트에 반환

이외에 다룰 만한 주제

  • 데이터베이스 규모 확장
    • 스케일 업 vs 스케일 아웃
    • SQL vs NoSQL
    • master-slave 다중화
    • 복제본(replica)에 대한 읽기 연산
    • 일관성 모델
      • 데이터 일관성: DB에 데이터를 넣고 바로 조회를 했을 때 이전 데이터를 반환할 수도 있음 → 데이터 일관성을 갖지 않음
        → 내가 행한 동작에 대해 반드시 결과를 내준다면 데이터 일관성을 갖는 것
    • DB 샤딩
  • 웹계층 무상태(stateless) 운영
  • 많은 데이터 캐시 할 방법
  • 메시지 큐를 사용하여 컴포넌트 사이 결합도 낮추기
  • 핵심 메트릭(트래픽이 몰리는 시간대의 Queries Per Second, 피드 새로고침 시 지연시간)에 대한 모니터링

[12장] 채팅 시스템 설계

개략적 설계안

  • 채팅 서비스의 기본 기능
    • 클라이언트들로 부터 메시지 수신
    • 메시지 수신자 결정 및 전달
    • 수신자가 접속중이 아닐 경우 접속할 때까지 메시지 보관
  • 서버 - 클라이언트 통신 방식: 웹소켓
    • 서버가 클라이언트에게 비동기 메시지를 보낼 때 가장 널리 사용하는 기술
    • 처음에 HTTP 연결 후 handshake 절차 이후 웹 소켓 연결로 업그레이드
    • 양방향 연결, HTTP/HTTPS와 같은 80/443 포트를 그대로 사용, 송수신 시 동일 프로토콜 사용
    • BUT 연결이 항구적이므로 서버에서 연결 관리를 효율적으로 해야함-
  • 다이어그램 image
    • 채팅을 제외한 대부분 기능은 일반적인 HTTP로 구현
    • 크게 stateless 서비스, stateful 서비스, third-party 연동 이렇게 3가지로 구성
    • stateless 서비스
      • 로그인, 회원가입 등 대부분의 요청/응답
      • 무상태 서비스는 로드밸런서 뒤에 위치
      • 서비스 탐색 서비스는 클라이언트가 접속할 채팅 서버의 DNS 호스트 명을 알려주는 서비스
    • stateful 서비스
      • 채팅서비스
      • 각 클라이언트와 채팅서버는 독립적인 네트워크 연결 유지
      • 서비스 탐색서비스는 특정 채팅 서버에 부하가 몰리지 않도록 도움
    • third-party 연동
      • 푸시알림
      • 앱이 실행 중이 아니더라도 알림 받아야함
  • 규모 확장성을 고려한 설계 image
    • 채팅서버는 클라이언트 간 메시지 중계 담당
    • 접속상태 서버는 사용자 접속 여부 관리
    • API 서버는 나머지 전부를 처리
  • 저장소
    • 채팅 시스템의 데이터는 아래 두가지로 나뉨

      • 사용자 프로필, 설정, 친구 목록 등 일반적인 데이터
      • 채팅 이력
    • 일반 데이터는 데이터 안정성을 보장하는 RDBMS에 보관

    • 채팅이력은 읽기/쓰기 연산 패턴을 이해해야함
      - 채팅 이력 데이터는 매우 거대, 대부분 최근 메시지가 조회됨
      - 이전 메시지로 점프, 메시지 검색 등 이전 데이터 접근도 가능해야함
      - 1:1 채팅은 읽기 쓰기 비율이 1:1

      키-값 저장소

    • 수평적 규모 확장이 용이

    • 데이터 접근 지연시간이 낮음

    • RDBMS는 long tail에 해당하는 부분을 잘 처리하지 못함(index가 커지면 access 비용 증가)

      • long tail: 상위 20%가 전체 80%를 차지하는데(파레토 법칙), 대부분 사용자는 최근 메시지에 접근하며, longtail은 오래된 메시지를 말함
    • 대표적으로는 HBase, 카산드라 등이 사용됨

상세 설계

  • 서비스 탐색
    • 서비스 탐색 기능의 역할로는 클라이언트에게 가장 적합한 채팅 서버를 추천하는 것

    • 이때 사용자의 위치, 서버의 용량 등이 고려됨

      → Apache Zookeeper를 주로 사용

      image

    1. 사용자가 로그인 시도
    2. 로드밸런서가 로그인 요청을 API 서버들 가운데 하나로 보냄
    3. API 서버가 사용자 인증을 처리한 뒤 서비스 탐색 기능이 동작하여 최적의 채팅서버를 찾음
    4. 사용자는 서비스 탐색 기능에서 찾은 서버와 웹소켓 연결을 맺음
  • 메시지 전달 흐름
    • 1:1 채팅 image
    • 여러 단말 사이의 메시지 동기화 방법 image
      • 각 단말들은 채팅 서버 1에 웹소켓 연결
      • 단말은 cur_max_message_id 라는 변수를 통해 해당 단말의 가장 최신 메시지 ID를 추적
      • 수신자 ID가 현재 로그인한 사용자 ID와 같고, key-value 저장소에 저장된 메시지의 ID가 cur_max_message_id 보다 크면 새 메시지로 간주
      • cur_max_message_id는 단말마다 별도로 유지 관리하면 되므로 동기화 작업 쉽게 구현 가능
    • 소규모 그룹 채팅에서의 메시지 흐름 image
      • A가 보낸 메시지가 사용자 B와 C의 메시지 동기화 큐에 복사

      • 그룹이 크기 않으면 메시지를 수신자별로 복사해서 큐에 넣는 작업의 비용이 크기 않음

        image

      • 한 사용자는 여러 사용자로부터 온 메시지를 받아야 하므로, 동기화 큐는 여러 사용자로부터 오는 메시지를 받을 수 있어야 함

  • 사용자 접속 상태 표시 방법 사용자는 아래 시나리오에서 접속 상태가 변경됨
    • 사용자 로그인: 로그인 시 접속상태 서버와 웹소켓 연결을 맺고, 저장소에 A의 상태를 online 으로 변경 및 최근 활동 시간 값 보관

    • 로그인: 저장소에 A의 상태를 ofline으로 변경

    • 접속 장애
      - 간단한 방법은 연결이 끊어지면 ofline 상태로 표시하고 복구 시 online으로 변경하는 것
      - 하지만, 짧은 시간 인터넷 연결이 끊어졌다 연결되는 모든 상황에서 상태 변경은 별로임
      - heartbeat 검사를 통해 문제 해결 → 주기적으로 heartbeat event를 접속상태 서버로 보내고, 마지막 이벤트를 받은 지 x초 이내에 또 다른 heartbeat event를 받으면 온라인 유지

      접속 상태 정보의 전송

      image

    • 접속상태 서버는 publish - subscribe 모델

      • 각 친구관계마다 채널을 하나씩 둔다.
      • 위 예시에서는 A-B, A-C, A-D 채널이 존재
      • 각 채널을 각 친구들이 구독
    • 이는 그룹의 크기가 작을 때 효과적, 동일 이벤트가 친구 수만큼 발생

    • 그룹이 클 경우 사용자가 그룹 채팅에 입장하는 순간에만 상태정보를 읽거나 사용자가 친구의 접속상태를 수동으로 갱신하는 방식을 사용

profile
DB를 사랑하는 백엔드 개발자입니다. 열심히 공부하고 열심히 기록합니다.

0개의 댓글