https://www.youtube.com/watch?v=_F6k0tg8ODo
당근 테크 밋업을 보고 정리한 글입니다.
N개의 서버를 사용하는 구조에서 채팅 구현하기
A라는 사용자와 B라는 사용자가 통신을 하기 위해서는 서로의 세션이 어느 서버에 있는지 알아야한다.
이를 위하여, 공유 메모리를 사용했다.
유저 A가 메세지를 발행하고, B 가 어느 서버에 있는지 메모리 저장소를 통해 알아온다.
아, B 는 서버 2에있고 IP는 어디니깐, 메세지를 발행한다.
하지만 만약에 서버가 10대로 늘어난다면
서버 to 서버로 통신을 하다보니, 네트워크의 복잡도가 증가한다.
그래서 당근은 확장에 유리한 구조를 가져가기 위해 메세지 큐를 도입했다.
A가 메세지를 보내서 B에게 메세지를 보내달라고 요청하는 이벤트를에 넣는다 ⇒ consumer가 key-value에 접근하고 => 서버2에 요청을함.
메세지 큐와 consumer를 도입해서, 서버 1이 서버2와 직접 통신하지 않게 된다.
또한 비동기로 처리를 하기에 응답을 기다리지 않아도 된다.
푸쉬 알림 구현하기
그렇다면 푸쉬는 언제 보내야할까?
- 상대방이 미접속 유저라면
- key - value에서 해당 유저는 미접속이구나 확인 후
- consumer가 push 서버에 요청을 함.
- key-value store 데이터가 오류가 있다면?
- 미접속 유저인데 접속 중이라고 판별 해서 서버로 요청했는데
- 서버가 확인했을 때 유저가 없으면, 서버가 푸쉬를 보내고, Key-value에서 얘 연결 중이지 않음 이라고 값을 삭제한다.
- 서버 다운시 이런 상황 발생 가능
내 의견) 뭔가 푸쉬 처리를 위한 아키텍처가 흩어져있는 느낌이다. 차라리 서버가 푸쉬를 전송하지 않고 해당 유저가 없다면 이벤트 큐에 얘는 푸쉬 메세지를 보내야한다의 이벤트를 발행하면 푸쉬를 담당하는 역할은 컨슈머만 가져가서 좋을 것 같다는 생각이 든다.
당근 모임 그룹 채팅
데이터 베이스에서 참여중인 모든 멤버들(10명)을 조회하고,
10번 이벤트를 발행해서 하려고 했음. 만약 유저가 1,000명이라면 이벤트 1000건을 발행해야함.
해결책 - user id 를 묶어서 요청하자
메세지 큐에 배열로 모아서 이벤트를 요청 한다.
컨슈머가 각 유저가 어느 서버에 있는지 잘 구별을 해줘야된다.
처리에 시간이 오래 걸린다면?
user id를 chunk 단위로 나누자.
꼭 1개가 아닌 5개 정도로 나눌 수 있도록 한다.
적절한 프로세스 시간을 측정해서 그 단위를 알아본다.
메세지 저장 시스템
기존에는 RDB를 통해서 스케일업을 통해 해결했지만 한계가 있었음.
그래서 메세지 구조를 재설계함.
- 샤딩
- 조건 - 가능한 적은 샤드에서 데이터를 모두 가지고 오게해야함
그러기 위해선 샤드 키 선택이 중요했는데,
- 어떻게 골랐을까?
- 같은 채팅방 안의 메세지들은 같은 샤드에 넣는다.
- 채팅방 아이디를 기준으로 샤딩 함.
- 유저 정보, 유저의 채팅방의 경우 유저아이디를 기반으로 샤딩 함.
- 구현을 어떻게 함?
- DynamoDB로 변경
- DynamoDB 는 개발자가 추가 구현을 하지 않더라도 파티션 키를 기준으로 내부적으로 샤딩되기 때문에, 적합했음.
내 의견 ) 우리 회사도 dynamodb 로 채팅을 저장하는걸로 알고 있고 채팅방 아이디로 파티션 키를 사용하는걸로 알고 있어서 신기했다.ㅋㅋ
채팅 실시간 API 최적화 - By 고루틴
1. display_idx
채팅방들을 정렬 하는 용도로, display_idx 라는 필드를 만들어서 정렬키로 사용함.
메세지 전송이 들어왓을 때 이 dispaly_idx를 업데이트 해주는건 멀티 스레드로 관리한다.
고루틴을 사용하여 스케줄러가 os스레드에 매핑한다
부분적으로 실패한다래도 새로운 메세지만 날라와도 바로 정상 작동 가능
2. 여러 자원들 불러오기
다른 곳에서 조회하는 부분을 고루틴을 통해 병렬로 읽고 핸들러가 합쳐준다
우리 회사도 멀티 스레드와 이벤트 큐를 도입하면 참 좋겠다는 생각을 한다.
어떤 자원을 기다리기 위해 동기 처리로 짜여있는 코드가 많은데 굳이 그럴 필요가 없는 경우를 많이 봤기 떄문이다.
또한 중복된 로직이 너무 많아서, 이벤트 발행을 쓰면 유지 보수에도 좋을 것 같았다.
예를 들어 어떤 함수에서 포인트 지급을 하는데 포인트 지급을 기다릴 필요가 없는데 기다리고 있는 등..
이벤트 발행으로 비동기적으로 처리하면 효율적이지 않을까
물론 잘 굴러가는 서비스를 건들일 이유는 없지만 다음에 문제가 생긴다면 고려해봐야겠다.
더욱이 회사 채팅 서비스도 꽤 규모가 큰데, 코드를 구경해봐야겠다.
'개발 아카이브' 카테고리의 다른 글
Grafana LGTM 톺아보기 (2) - Alloy 란? (7) | 2025.02.02 |
---|---|
Grafana LGTM 톺아보기 (1) - Loki 란? (5) | 2025.01.19 |
slack 파일 업로드, completeUploadExternal(),getUploadURLExternal() 사용법 (3) | 2024.11.19 |
Node.js 에서 Cpu intensive한 코드 찾아내는 법 강연 정리 (3) | 2024.10.27 |
쿠버네티스란? feat. 데이터 처리도 이제는 컨테이너로, 우아한형제들의 데이터플랫폼 혁신 (5) | 2024.10.13 |