ASHD Dev_Blog

2. 주변 친구 [SYS-study2]

단일 서버 구조에서 사용자 수가 증가함에 따라 추가해야 할 설계

이재룡
이재룡 Jul 2, 2025

[ 설계 고려 사항 ]

  • 가깝다의 정의
  • 위치 갱신 주기
  • 이동 이력 보관 여부
  • 비활성 상태에 대한 처리 (마지막 위치 or 표기X)
  • low latency
  • 최대 몇 명을 보여줄지
 

[ 프로토콜 ]

  • 위치정보를 지속적으로 단일 유저가 아닌, 친구로 등록된 모든 유저에게 전송
 
  1. 공용 백엔드 (큰 규모에서는 SPOF)
      • 기본적으로 모든 사용자의 위치 수집
      • 사용자끼리 너무 멀어지면 수집 X
      • 양방향 실시간 위치 정보의 경우 웹소켓 도입
  1. 데이터 베이스
      • 사용자 DB (RDB or NoSQL) : 친구관계정보
      • 이동이력 DB (RDB or NoSQL)
      • redis pub/sub : 위치변경 이벤트
 
Callout icon'

소켓의 핸들러가 pub/sub의 Subscriber!!!

 
  1. 클라이언트가 위치 변경 요청을 하면 ( client → LB )
  1. 로드밸런서에서 해당 요청을 기반으로 소켓 서버에 연결 ( LB → SocketServer )
Callout icon'

소켓 연결은 클라이언트-소켓 서버에 다이렉트 연결(다중이어도 동일함)인데 로드밸런서 라우팅이 필요한가?
→ 여유 있는 소켓 서버에 클라이언트 분할 연결

  1. 이동 이력 저장 + 위치 정보 캐싱 ( socketServer → DB / cache )
  1. redis pub/sub 위치 변경 이벤트 발행 (socketServer → redis)
  1. 웹소켓 이벤트 핸들러 에서 이벤트 수신 (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 된 = 접속중이 아닌 = 채널이 끊긴
Callout icon'

OFF된 친구에게 내 정보를 어떻게 강제로 줘야할까?

 

기존 친구 채널 시스템에서는

  • 친구 리스트를 전부 가져오고
  • 거리 판별(X)
  • redis 사용
    • 사용자 정보를 redis 캐싱을 통해 저장 + TTL 설정
    • redis pub/sub 이벤트 발행
  • OFF가 한참된 친구 = TTL로 만료되어 캐시에서 제거
    •  

      방안 1. db 안의 history 참고 → 해당 유저의 최종 history를 찾아서 return

      • 샤딩되어 있어도, 어차피 유저 정보 찾으러 가야 하니까 찾으면 만사해결? (cache-miss)

       

      방안 2. redis의 ttl을 길게 배치하고, 해당 사용자의 친구 수만큼 read count 부여하고 count 소모시 제거?

      • redis의 캐시가 빠르게 가득 찰 수도? (삭제가 덜 자주 일어남)

       
       

추천 글

BlogPro logo