가상 면접 사례로 배우는 대규모 시스템 설계 기초 2 | 02장. 주변 친구
‘주변 친구(Nearby friends)‘라는 모바일 앱 기능을 지원하는 규모 확장이 용이한 백엔드 시스템을 설계해 보겠다.
1단계: 문제 이해 및 설계 범위 확정
- ‘주변에 있다’는 직선 거리로 5마일(mile) 내에 있을 시를 말한다.
- 이 수치는 설정이 가능하다.
- 사용자의 이동 이력을 저장한다.
- 기계 학습(machine learning) 등 다양한 용도로 사용될 수 있을 것이다.
- 사용자가 10분 이상 비활성 상태면 주변 친구 목록에서 사라진다.
- 사생활 데이터 보호범도 고려하지 않는다.
- 복잡해서 제외한다.
기능 요구 사항
- 사용자는 모바일 앱에서 주변 친구를 확인할 수 있어야 한다.
- 해당 친구까지의 거리, 해당 정보가 마지막으로 갱신된 시각(timestamp) 표시
- 이 친구 목록을 몇 초마다 갱신되어야 한다.
비기능 요구 사항
- 낮은 지연 시간(low latency)
- 주변 친구의 위치 변화가 오래 걸리면 안된다.
- 안정성
- 전반적으로 안정적이어야 하지만, 때로는 몇 개의 데이터 정도 유실되도 된다.
- 결과적 일관성
- 위치 데이터를 저장하는데 강한 일관성을 지원할 필요는 없다.
- 복제본의 데이터가 원본과 동일하게 변경될때까지는 몇 초정 정도 걸려도 된다.
개략적 규모 추정
- ‘주변 친구’는 5마일 반경 이내 친구로 정의
- 친구 위치 정보는 30초 주기
- 사람이 빨리 걷지는 않기 때문에 30초면 충분
- 하루 주변 친구 검색 기능 활용할 사용자 1억명으로 가정
- 동시 접속자 사용자 수는 10% 가정
- 10% * 1억 = 천만명
- 평균적 한 사용자가 4000명의 친구를 갖는다고 가정
- 이 기능은 페이지당 20명의 주변 친구를 표시
- 요청이 있으면 더 많은 친구를 보여준다.
2단계: 개략적 설계안 제시 및 동의 구하기
다른 장과 다르게 위치 정보를 모든 친구에 보내줘야 해서 서버-클라이언트 간에 HTTP 프로토콜을 사용할 수 없다는 것을 감안하여, 먼저 개략적 설계안를 이해하지 못하면 어떤 API를 만들어야 할지 알기 어렵기 때문에, 계략적 설계안부터 살펴본다.
계략적 설계안
- 모든 활성 상태 사용자의 위치 변화 내역을 수신한다.
- 사용자 위치가 변경 될때마다, 활성 상태인 친구에서 변경 내역 절달한다.
- 두 사용자의 사이가 먼 경우에는 변경 내역을 전송
문제는 큰 규모에 적용하기 쉽지 않다.
- 활성 상태의 동시 접속 사용자가 천만 명 정도에 위치 정보를 30초마다 갱신하면 초당 334,000번 위치 정보 갱신
- TPS : 10,000,000 트랜잭션 / 30초 = 3,333,333.33333TPS(약 334,000번)
- 평균 사용자가 1명은 400명 친구를 갖고, 그 가운데 10%가 인근 활성화 상태라고 가정하면 1,400만건 위치 정보 갱신 요청
- 초당 334,000번 * 400명 * 10% = 1,400만
- 이 엄청난 양의 갱신 내역을 사용자 단말로 보내야 한다.
설계안
- 로드 밴런스
- WebSocket 및 HTTP 서버 앞단에 위치
- 부하를 고르게 분산하기 위해 트래픽을 서버들에 배분하는 역할
- RESTful API 서버
- 친구를 추가/삭제하거나 사용자 정보를 갱신하는 등의 부가적인 작업을 처리
- 웹 소켓 서버
- 친구 위치 정보 변경을 실시간으로 상태를 유지하는 서버 클러스터
- 각 클라이언트는 한대의 웹소켓 연결을 지속적으로 유지
- 위치 변경되는 내역을 이 연결로 전송
- 친구 위치 정보 변경을 실시간으로 상태를 유지하는 서버 클러스터
- 레디스 위치 정보 캐시
- 활성 상태의 사용자의 가장 최근 위치 정보를 캐시하는데 사용
- TTL을 지정하여, 시간이 지나면 위치 정보 캐시 삭제
- 캐시 정보 갱신될마다 TTL도 갱신
- 사용자 데이터베이스
- 사용자 데이터 및 사용자의 친구 관계 정보도 저장
- RDB, NoSQL 어느 쪽이든 사용 가능
- 위치 이동 이력 데이터베이스
- 사용자의 위치 변경 이력 보관
- 레디스 펍/섭 서버
- 초경략 메세시 버스로 Redis Pub/Sub 사용
주기적 위치 갱신
- 클라이언트 위치 변경 사실을 로드밸런서에 전송
- 로드밸러서는 그 위치 변경 내역을 웹소캣으로 전송
- 웹소켓 서버는 해당 이벤트를 위치 이동 데이터베이스에 저장
- 웹소켓 서버는 캐시에 저장. TTL도 갱신
- 웹소켓 서버는 레이스 펍/섭 서버에 해당 사용자 채널에 위치 발행. 3~5는 병렬로 수행
- 레디스 펍/섭 모든 구독자(온라인 친구)에게 브로드캐스트
- 새 위치를 보낸 사용자와 메세지를 받은 사용자의 사이 거리 새로 계산
- 7에서 계산한 거리가 검색 반경에 넘지 않으면 각 클라이언트에 정보를 전송하고, 넘으면 전송 안함
API 설계
- 서버 API - 주기적인 위치 갱신
- 클라이언트 API - 클라이언트 갱신된 친구 위치를 수신하는 데 사용할 API
- 서버 API - 웹소켓 초기화 API
- 클라이언트 API - 새 친구 구독 API
- 클라이언트 API - 구독 해지 API
데이터 모델
위치 정보 캐시
- 키 : 사용자 ID
- 값 : {위치, 경도, 시각}
현재 정보만 저장.
레디스는 활성화 사용자를 TTL를 지정해서 저장하여 적합.
위치 이동 이력 데이터베이스
- 컬럼 : 사용자 ID, 위치, 경도, 시각
대용량 처리를 요구될 수록 샤딩이 필요한 데이터베이스가 필요.
3단계: 상세 설계
중요 구성요소별 규모 확장성
API 서버
- 클러스터를 CPU, I/O 사용률에 따라 자동으로 눌리는 방법은 다양하다.
웹소켓 서버
- 사용률에 따라 규모를 자동으로 늘리는 것은 어렵지 않다.
- 자동으로 확장하려면 로드밸런스에서 처리 가능하다.
클라이언트 초기화
- 위치 정보 캐시 갱신한다.
- 모든 친구 정보를 가져온다.
- 활성화된 모든 친구의 위치 정보를 한번에 가져온다.
- 비활성된 사용자는 이미 TLL로 인해 없을 것이다.
- 전달 받은 친구 위치 각각에 대해 웹소켓 연결한다.
- 웹소켓 서버는 각 친구의 레디스 서버 펍/섭 채널을 구독한다.
- 사용자의 현재 위를 레디스 펍/섭 서버의 전용 채널을 통해 모든 친구에게 전송한다.
사용자 데이터베이스
- 저장되는 데이터
- 사용자 ID, 사용자명, 프로파일 이미지의 URL 등
- 친구 관데 데이터
- 대용량을 위한 사용자 ID로 샤딩된 데이터베이스 필요
- 규모의 크다면, 실제로를 사용자 및 친구 데이터를 관리하는 팀이 따로 필요
위치 정보 캐시
- 대용량 처리를 하려며 사용자 ID를 기준으로 여러 서버에 샤딩하면 부하를 분배할 수 있다.
- 가용성을 높이기려면, 대기 노드를 복제하여, 주 노드가 장애가 나면 대기 노드의 주 노드 승격으로 장애 시간을 줄인다.
레디스 펍/섭 서버
- 레디스 펍/섭을 선택한 이유는 채널을 만드는 비용이 아주 저렴하기 때문이다.
- 레디스 펍/섭의 병목은 메모리가 아니라 CPU 사용량이다.
- 예상되는 메모리 사용량은 200GB 정도 될거 같고, 100GB의 설치할 수 있는 서버를 사용하면 된다.
- 위치 정보 갱신은 초당 1400만건 / 서버 한 대로 감당 가능한 구독자 수는 100,000명 = 140대 서버 예상
- 대규모를 감당하려면, 분산 레디스 펍/섭 클러스터가 필요하다.
분산 레디스 펍/섭 서버 클러스터
- 메세지를 발행할 사용ID를 기준으로 펍/섭 서버들을 샤딩하면 된다.
- 모든 채널은 서로 독립적이다.
- 운영에는 서비스 탐색(service discovery) 컨포넌트를 도입하여 이 문제를 해결할 수 있다.
- etcd, 주키퍼 등이 있다.
- 방안으로 안정 해시(consistent hash)의 해시 링를 참조한다.
친구 추가/삭제
- 친구 추가/삭제시에 콜백을 해당 앱에 등록해 둘 수 있다.
- 이 콜백은 호출되면 웹소켓 서버로 친구의 펍/섭 채널을 구독/구독 해지 하라고 메세지를 보낸다.
친구가 많은 사용자
- 친구 5000명까지만 처리, 방향을 갖는 팔로어 모델처럼 단방향은 논의 배제
- 수천 명의 친구를 구독하는 데 필요한 펍/섭 구독 관계는 클러스터 내의 많은 웹소켓 서버에 분산
- 부하는 각 소켓 서버가 나누어 처리하므로 핫스팟 문제는 발생하지 않을 것이다.
주변의 임의 사용자
- 지오해시에 따라 구축된 펍/섭 채널 풀을 둔다.
- 상세 내용 생략
레디스 펍/섭 외의 대안
- 얼랭(Erlang)이 대안으로 대체 될 수 있다.
- 상세 내용 생략
4단계: 마무리
- 사용자의 위치 정보 변경 내역을 그 친구에게 효율적으로 전달하는 시스템 설계하였다.
- 웹소켓, 레디스, 레디스 펍/섭이 이 설계안의 핵심 컴포넌트이다.
- 소규모에서 규모가 커짐에 따른 해결책도 살펴봤다.
최종 수정 : 2024-02-17