가상 면접 사례로 배우는 대규모 시스테 설계 기초 #10 (알림 시스템 설계)

박주진·2022년 6월 11일
0

문제 이해 및 설계 범위 확정

어떤 종류의 알림을 지원해야 하나요?

  • 푸시, sms, email

실시간 시스템이어야 하나요?

  • 연성 실시간 시스템 (soft real-time)이라 가정합니다. 가능한 빨리 알림이 전달되어야 하지만 시스템에 높은 부하가 걸렸을때 약간의 지연은 무방하다.

어떤 종류의 단말을 지원해야 하나요?

  • ios, android, laptop, desktop

사용자에게 보낼 알림은 누가 만들 수 있나요?

  • client 애플리케이션 또는 서버 측 스케줄링이 가능함

사용자가 알림을 받지 않도록 (opt-out)설정 가능?

  • 넵 해당 설정시 사용자는 더 이상 알림을 받지 않습니다.

하루에 몇 건의 알림을 보낼 수 있나요?

  • 천만건 모바일 푸시, 백만건 sms, 오백만건의 이메일

개략적 설계안 제시 및 동의 구하기

알림 유형별 지원 방안

IOS 푸시 알림

알림 제공자 -> APNs -> IOS 단말

  • 알림 제공자: 알림 요청을 만들어 APNS (Apple Push Notification Service)로 보내는 주체이다. 요청을 만들려면 아래와 같은 데이터가 필요하다.

    • 단말토큰: 알림 요청을 보내는 데 필요한 고유 식별자
    • 페이로드: 알림 내용을 담은 JOSN 객체
    {
     "aps" {
    		"alert": {
          	"title": "Game Request",
              "body": "Bob wants to play"
              "action-lock-key": "PLAY"
          },
          "badge": 5
    
     }
    }
  • APNS: 애플이 제공하는 원격 서비스. 푸시 알림을 IOS 장치로 보내는 역할

  • IOS 단말: 푸시 알림을 수신하는 사용자 단말

Android 푸시 알림

알림 제공자 -> FCM -> 안드로이드 단말

  • IOS다른 점은 FCM (firebase cloud messaging)을 이용한다는 점

SMS 메시지

알림 제공자 -> SMS 서비스 (Twillo, Nexmo) -> SMS 수신 단말

이메일

알림 제공자 -> 이메일 서비스 -> 이메일 수신 단말

  • 책에는 이메일 수신 단말 즉 휴대폰 단말 처럼 특정 단말로 수신되는건 처럼 표현되었지만 실제로는 수신측 메일 서버에 보관 되었다가 수신자가 해당 메일 서버에 수신된 자기 메일을 웹이나 어플리케이션으로 확인 하는 것이다.

연락처 정보 수집 절차

  • 알림을 보내기 위해서는 단말 토큰, 전화번호, 이메일 주소 등의 정보가 필요하다.
  • 처음 계정 등록 또는 앱 설치시 사용자 정보를 수집하여 데이터 베이스에 저장한다.
USER
user_id (bigint)
email (varchar)
country_code (integer)
phone_number (integer)
created_at (timestamp)
DEVICE
id (bigint)
device_token (varchar)
user_id (bigint)
last_logged_in_at (timestamp)
  • 테이블 설계는 대략적으로 위에 같인 진행한다. 사용자가 여러 단말을 가질 수 있기 모든 단말에 전송되어야 한다는 점을 고려하여 device테이블을 분리한다.

알림 전송 및 수신 절차

개략적 설계 초안

  • 1부터 N까지 서비스: 마이크로 서비스, 크론, 분산 시스템 컴포넌트등 다양한 서비스가 될 수 있다.
  • 알림 시스템: 1개라고 가정한다. 외부 서비스에 알림 전송을 API를 제공하고 제3자 서비스에 전달할 알림 페이로드를 만들어 낼 수 있어야 한다.
  • 제3자 서비스: 실제 사용자에게 알림을 전달하는 역할을 담당한다. 제3자 서비스와 통합을 진행할때 유의할 것은 확장성이다. 쉽게 새로운 서비스를 통합 또는 제거할 수 있어야 한다. 그리고 특정 서비스는 다른 시장에서 사용할 수 없을 수 있다. 예) 중국에서는 FCM 사용불가 Jpush, PushY를 이용해야 함.
  • 단말: 사용자는 자기 단말에서 알림을 수신
문제점
  • SPOF: 알림 서비스가 한대여서 장애시 전체 장애로 이어짐
  • 규모 확장성: 서버 한대로 푸시 알림과 관계된 모든 작업을 처리함으로, 데이터 베이스나 캐시 등 중요 컴포넌트의 규모를 개별적으로 늘릴수 없다.
  • 성능 병목: 알림을 처리하고 보내는건 많은 자원이 필요하다. 예를 들어 HTML 페이지를 만들고 제 3자 서비스의 응답을 기다리는일은 시간이 많이 걸릴 가능성이 있다. 따라서 한 서버에서 처리하면 트래픽이 몰리는 시간에 과부하 여지가 있다.

개략적 설계 (개선안)

초안의 문제점을 다음과 같이 개선한다.
  • 데이터베이스와 캐시를 알림 시스템의 주 서버에서 분리하여 알림 시스템과, 데이터 베이스, 캐시를 개별적으로 확장할 수 있게 한다.
  • 알림 서버를 수평적 규모 확장이 이루어질 수 있도록 한다.
  • 메시지 큐를 이용해 시스템 컴포넌트 사이의 강결합을 끊는다. (재시도, 여러 알림 병렬처리, 버퍼 역할 등 추가적인 장점이 있는듯 하다.)
  • 1부터 N까지 서비스: API로 알림을 보낼 서비스
  • 알림 서버
    • 알림 전송 API: 스팸 방지를 위해 인증된 클라이언트만 이용하게 제한
    • 알림 검증: 이메일 주소, 전화번호 등에 대한 기본 검증
    • 데이터 베이스 또는 캐시 질의: 알림에 필요한 데이터 가져오기
    • 알림 전송: 알림 데이터를 메시지 큐에 넣는다.
  • 캐시: 사용자 정보, 단말 정보, 알림 템플릿 등을 캐시한다.
  • DB: 사용자, 알림, 설정 등 다양한 정보 저장
  • 메시지 큐
    • 시스템 컴포넌트 간 의존성 제거 (외부 서비스를 알림 서버가 직접 호출하지 않음으로 의존하지 않게 된다)
    • 다량의 알림이 전송되어야 하는 경우 버펴 역할
    • 알림 종류별로 메시지 큐가 분리되어 있어 하나의 장애가 다른 알림에 영향을 주지 않는다.
  • 작업 서버: 메시지 큐에서 알림을 꺼내 제3자 서비스로 전달
  • 제3자 서비스: 이전과 동일
  • 단말: 사용자는 이전과 동일
알림 전송 프로세스
  1. API를 호출하여 알림 서버로 알림을 보낸다.
  2. 알림 서버는 캐시나 데이터 베이스 질의를 통해 메타 데이터(사용자 정보, 단말토큰, 알림설정)를 가져온다.
  3. 알림 서버는 전송할 알림에 맞는 이벤트를 만들어 적절한 큐에 넣는다.
  4. 작업 서버는 메시지 큐에서 알림 이벤트를 꺼낸다.
  5. 작업 서버는 알림을 제3자 서비스로 보낸다.
  6. 제3자 서비스는 사용자 단말로 알림을 전송한다.

상세 설계

추가적으로 자세히 살펴볼 사항들

안정성

데이터 손실 방지

  • 알림이 지연되거나 순서가 틀려도 무방하지만 사라지면 안된다.
  • 별도로 알림 로그와 같은 데이터 베이스를 두고 재시도 메커니즘을 구현한는 것도 방법이다.

알림 중복 전송 방지

  • 분산 시스템 특성상 같은 알림이 여러 번 반복되는 것을 100% 막는것은 불가능하다.
  • 중복 발송 빈도를 줄이기 위해서 아래와 같은 프로세스를 추가 할 수 있다.
    • 보내야 할 알림이 도착하면 이벤트 ID를 검사하여 중복 발송이면 버리고 아니면 발송한다.

추가로 필요한 컴포넌트 및 고려사항

알림 템플릿

  • 알림 메시지 대부분 형식이 비슷하다.
  • 알림 템플릿 인자, 스타일, 링크만 조정하여 사전에 지정한 형식에 맞춰 알림을 만들어내는 틀이다.
  • 형식을 일관성 있게 유지할 수 있고, 오류 가능성 알림 작성에 드는 시간도 줄일 수 있다.

알림 설정

  • 사용자는 이미 너무 많은 알림을 받고 있어 피로함을 늒니다.
  • 알림 설정을 상세히 조정할 수 있도록 하고 이 정보를 테이블에 보관한다.
  • 알림 발송전 알림이 켜져 있는지 반드시 확인한다.

전송률 제한

  • 한 사용자가 너무 많은 알림을 보내지 않도록 빈도를 제한 한다.

재시도 방법

  • 알림 전송에 실패하면 재시도 전용 큐에 넣는다
  • 같은 문제가 지속적을 발생하면 개발자에게 통지한다.

푸시 알림과 보안

  • ios, 안드로이드 앱의 경우 알림 전송 api는 appKey와 appScret을 사용하여 보완을 유지한다.
  • 인증도니 혹은 승인된 클라이언트만 api로 알림 발송 가능

큐 모니터링

  • 큐에 쌓인 알림의 개수 모니터링이 중요하다.
  • 쌓인 알림의 개수가 너무 크면 서버가 빠르게 처리하고 있지 못하다는 뜻이다.

이벤트 추적

  • 알림 확인율, 클릭율, 실제 앱 사용으로 이어지는 비율 같은 메트릭은 사용자를 이해하는데 중요하다.
  • 고로 데이터 분석 서비스와 알림 시스템을 통합해야 한다.

수정된 설계안

추가된 컴포넌트

  • 알림 서버에 인증과 전송률 제한 기능을 추가 하였다.
  • 전송 실패에 대응하기 위한 재시도 기능 추가 (지정된 횟수만 큼만 재시도)
  • 전송 템플릿을 사용하여 알림 생성 과정을 단순화 하고 알림 내용을 일관성 있게 유지한다.
  • 모니터링과 추적 시스템을 이용하여 시스템 상태을 확인하고 시스템을 개선하기 쉽도록 하였다.

마무리

  • 알림은 중요 정보를 알려준다는 점에 있어서 꼭 필요한 기능이다.
  • 규모 확장이 쉽고, 결합도 낮은 그리고 다양한 정보 전달 방식을 지원하는 알림 시스템을 구축해 보았다.
  • 특히 아래 주제에 집중하였다.
    • 안정성: 전송 실패율을 낮추기 위해 재시도 매커니즘 도입
    • 보안: 인증된 클라이언트만 보낼 수 있도록 appKey, appSecret등 메커니즘 이용
    • 이번트 추적 및 모니터링: 알림 전반적인 과정 추적 및 시스템 상태 모니터링 할 수 있도록 구축
    • 사용자 설정: 사용자가 알림 수신 설정을 조절할 수 있는 구조
    • 전송률 제한: 사용자에게 보내는 알림 빈도를 제한할 수 있는 구조

추가

  • 이메일 수신 프로세스
  • 알림 서비스 재시도 메커니즘 상세
  • 왜 exactly once는 실현 불가능 인가?
    • 요점은 sender가 reciever에게 메시지를 보낸후 receiver로 부터 ack를 못 받은 경우 우리는 한번 더 보내서 ack를 받거나 아니면 안보내는 선택지 말고는 없다. ack를 못 받은 경우 실제로 reciever가 못받었을수도 있으나 받았는데 ack를 보내는 도중 네트워크 문제가 발생 했을수 있다.
    • 그래서 주로 at-least-once + 멱등한 api 또는 중복 제거 처리
    • 링크1
    • 링크2
    • 예시 (완벽히 똑같지는 않지만 이런 network timeout 이슈가 있을 수 있으나 무조건 한번만 처리되어야 하는 경우 예시)
    • 모든 네트워크 통신은 그럼 동일하지 않을까? 메시지 큐 뿐만아니라??
    • 카프카로 메시지 던질떄도 동일하지 않을까 내가 보냈는데 응답이 없으면 다시 보낼지도??

0개의 댓글