2. 주변 친구 [SYS-study2]
단일 서버 구조에서 사용자 수가 증가함에 따라 추가해야 할 설계
이재룡 Jul 2, 2025
[ 설계 고려 사항 ]
- 가깝다의 정의
- 위치 갱신 주기
- 이동 이력 보관 여부
- 비활성 상태에 대한 처리 (마지막 위치 or 표기X)
- low latency
- 최대 몇 명을 보여줄지
[ 프로토콜 ]
- 위치정보를 지속적으로 단일 유저가 아닌, 친구로 등록된 모든 유저에게 전송
- 공용 백엔드 (큰 규모에서는 SPOF)
- 기본적으로 모든 사용자의 위치 수집
- 사용자끼리 너무 멀어지면 수집 X
- 양방향 실시간 위치 정보의 경우 웹소켓 도입
- 데이터 베이스
- 사용자 DB (RDB or NoSQL) : 친구관계정보
- 이동이력 DB (RDB or NoSQL)
- redis pub/sub : 위치변경 이벤트
소켓의 핸들러가 pub/sub의 Subscriber!!!
- 클라이언트가 위치 변경 요청을 하면 ( client → LB )
- 로드밸런서에서 해당 요청을 기반으로 소켓 서버에 연결 ( LB → SocketServer )
소켓 연결은 클라이언트-소켓 서버에 다이렉트 연결(다중이어도 동일함)인데 로드밸런서 라우팅이 필요한가?
→ 여유 있는 소켓 서버에 클라이언트 분할 연결
- 이동 이력 저장 + 위치 정보 캐싱 ( socketServer → DB / cache )
- redis pub/sub 위치 변경 이벤트 발행 (socketServer → redis)
- 웹소켓 이벤트 핸들러 에서 이벤트 수신 (redis → socketServer)
[ 위치 변경 이벤트 ]
대상 사용자에게 배정된 pub/sub에서 발행
- 친구로 등록된 handler는 구독자로 설정
- 모든 친구를 호출
- 친구 검사 (활성 상태 + 거리 계산)
- 검색 반경 조건에 맞으면 → 갱신된 위치와 시각 정보 전송
(병렬 동작 : 이동 history db 저장)
[ 위치 정보 캐시에 redis를 사용하는 이유 ]
- 위치 정보는 “현재 위치”만 중요 (이력은 저장하더라도 현재 위치는 하나!)
- 읽기 및 쓰기 연산속도가 빠르기 때문에 단일 데이터를 관리하는데 유리
- 영속성 보장할 필요없이 → 문제가 생기면 update를 기다리면 됨
- ttl에 따른 오랜 정보 (reload 필요한 정보)를 자동 삭제
⇒ 비활성화 제한 시간(기획적 의도) = TTL
⇒ 갱신시 TTL도 똑같이 update
[ 서버 변경 중에 대한 처리 ]
기존 서버를 제거할때에 대한 고려사항
- 로드밸런서가 서버상태를 인식하고 (종료중)
- 더이상 추가적인 소켓 연결을 만들지 않고
- 모든 소켓연결이 종료되면 → 서버 종료
[ Service Discovery Component ]
서버들이 서로의 위치(IP, 포트)를 자동으로 찾는 기술 (like 전화번호부)
- etcd , zookeeper 등 (key-value 저장소!)
[ 임의의 사용자 (by 지오 해시) ]
- 대상 정보 공유에 동의한 무작위 사용자
- 지오 해시에 따라 pub/sub 채널 배치
기존은 한 user는
- 친구로 등록된 유저의 핸들러를 구독
임의의 사용자 구독 방식에서 user는
- 한 특정 구역을 구독 (9q8zn6) + 8방위 구역
- 친구와 무관하게 9개의 구역에서 위치 이벤트가 발생하면 해당 정보를 GET
[ Topic : Instagram Friend Map ]
1. 요구 조건 분석
- 범위 (친구 + 외부인)
- 검색 - pass
- 내 위치 친구 1명에게 강제 전송
- 멀어도 친구면 확인가능
- 친구가 아니면 같은 지역에서만
- 위치 공유 여부 선택 가능
- 관련 게시물 확인 가능 - pass
2. 웹소켓으로 줘야하는 정보
- 위치 정보 (위 경도 + 갱신 시간)
3. DB에서 가져와야하는 정보
- 내 친구 리스트 + 친구의 공개 여부 포함
4. 소켓 설계 (2-channel 구독 시스템)
- Geohash를 구독함으로써 근처 임의의 사용자 이동 이벤트 구독
- 친구 id를 구독함으로써 친구의 이동 이벤트 구독
5. 캐싱
- OFF 된 = 접속중이 아닌 = 채널이 끊긴
OFF된 친구에게 내 정보를 어떻게 강제로 줘야할까?
기존 친구 채널 시스템에서는
- 친구 리스트를 전부 가져오고
- 거리 판별(X)
- redis 사용
- 사용자 정보를 redis 캐싱을 통해 저장 + TTL 설정
- redis pub/sub 이벤트 발행
- OFF가 한참된 친구 = TTL로 만료되어 캐시에서 제거
- 샤딩되어 있어도, 어차피 유저 정보 찾으러 가야 하니까 찾으면 만사해결? (cache-miss)
- redis의 캐시가 빠르게 가득 찰 수도? (삭제가 덜 자주 일어남)
방안 1. db 안의 history 참고 → 해당 유저의 최종 history를 찾아서 return
방안 2. redis의 ttl을 길게 배치하고, 해당 사용자의 친구 수만큼 read count 부여하고 count 소모시 제거?