-
Line | 오픈채팅 시스템 설계📖 개발 공부 2024. 2. 18. 02:24
“LINE 오픈챗 서버가 100배 급증하는 트래픽을 다루는 방법”을 보면서
오픈 채팅 시스템 설계를 정리한 내용이다!오픈 채팅 시스템의 아키텍처는 다음과 같다.
- 채팅의 다양한 이벤트(메시지 전송, 메시지 반응, 메시지 읽음 등)들을 채팅 서버에 보낸다.
- 해당 이벤트들을 모두 스토리지에 저장한다.
- 그리고 Kafka에 해당 이벤트들을 발행한다.
- 푸시 서버는 해당 이벤트들을 소비하여, 사용자들에게 해당 이벤트가 발생했다고 알린다.
- 사용자는 해당 이벤트를 조회한다. 이때 스토리지에 접근하여, 새 메시지를 화면에 추가하는 액션을 실행한다.
이 아키텍처에서 트래픽이 급증할 수 있는 패턴들을 살펴보자.
Hot Pattern 1: 수십개의 메시지를 주고받는 핫 챗
만약 한 채팅에 5천명 이상의 사용자가 있다고 해보자.
이때 [6]의 fetch event 요청 수가 평소 대비 100배 이상 급증하여 스토리지에 큰 부하가 일어날 수 있다. → 핫 챗(Hot Chat)
(큰 부하라고 하면, 한 샤드에 전달되는 요청량이 3배 이상 급증하는 경우를 예로 들 수 있겠다.)
또한 채팅ID 기반으로 샤딩하여 데이터를 저장한다고 했을 때, 하나의 채팅 안에서 발생하는 데이터를 더이상 분산시킬 수 없다. 그렇기에 하나의 키로 몰리면 부하가 집중하게 된다. (Hot Key)
(이때 샤드의 복제본을 추가하는 경우를 고려해볼 수 있지만, 핫챗의 비중이 0.1%에 불과하여 오버헤드가 크다고 판단)
이때 핫챗 감지 및 스로틀링(detection & throttling)을 통해 하나의 샤드에 몰리는 부하를 효과적으로 줄일 수 있었다.
이는 핫챗을 탐지해서 해당 핫챗에서 발생하는 fetch event API 요청을 줄이는 방법이다.
- detection: kafka & bucket
fetch event가 요청될 때마다 kafka로 이벤트를 발행한다. 이때 push server가 소비하여 하나의 채팅에 최근 몇초동안 몇 개의 fetch 이벤트를 요청됐는지 실시간으로 (bucket에) 기록한다. - throttling: 이때 핫챗이라고 판단된 경우, redis에 기록한다. 핫챗이라면 서버 푸시를 확률적으로 스로틀링해서 보내지 않는 방법이다.
→ 핫챗 탐지 기준, 스로틀링 지속 시간, 스로틀링 적용 기준은 Central Dogma를 통해 조절한다.
이 방법을 통해 1초에 수십개의 이벤트가 발생하더라도 사용자에게 거의 영향을 주지 않고 핫챗의 fetch event API 요청량을 효과적으로 줄일 수 있다.
Hot Pattern 2: 오픈채팅 참여 요청 급증
1초에 수천개의 오픈챗 참여 요청이 온 경우, 부하가 급증할 수 있다. (예를 들어, 팔로워 수백만명을 보유하고 있는 인플루언서가 오픈채팅 링크를 공유한 경우)
참여 요청이 오면 채팅 멤버 데이터를 MySQL에 저장한다. 이때 INSERT 쿼리를 날리는데, 이때 이미 가입된 사용자인지 등을 검사하기 위해 INSERT 내 SELECT 쿼리를 사용하는 서브쿼리를 사용한다.
INSERT INTO db ... SELECT member WHERE NOT EXISTS ( SELECT id FROM db ... AND membership_state IN JOINED ) ...
이때 MySQL에 부하가 급증할 수도 있다.
바로 MySQL의 innodb_autoinc_lock_mode 락 모드 때문이다.
INSERT 내 SELECT 쿼리는 bulk insert로 취급한다.
만약 innodb_autoinc_lock_mode가 1(consecutive)로 설정되어있다면,
bulk insert 구문은 AUTO-INCREMET 값을 증가시키기 위해 테이블락을 잡는다. 한 오픈 채팅에 참여요청이 급증하면, AUTO-INCREMENT 테이블 락 경합도 같이 급증하여 CPU 사용률이 100%에 도달하여 응답 timeout이 나게 된다.
이 테이블락을 잡지 않기 위해 이를 innodb_autoinc_lock_mode 락 모드를 2(interleaved)로 변경한다면,
동시에 여러개의 값을 insert할 때 AUTO-INCREMENT 값이 연속적이지 않을 수 있지만
1초에 수천개의 요청이 들어와도, MySQL은 CPU 사용률을 10~20%로 유지할 수 있다.
또 다른 곳에서 부하가 발생할 수 있다.
동시에 오픈챗 참여 요청이 많아질 경우를 대비하여 MySQL 처리량 한계를 넘지 않도록 이미 조인 스로틀링을 적용한 상황이라고 해보자.
이또한 핫챗 스로틀링과 비슷하게 푸시 서버에서 Kafka 이벤트를 소비하여 버킷에 기록하고, MySQL 처리량 한계를 넘으면 실패하도록 한다.
이때, Kafka를 사용하는 조인 스로틀링은 지연이 발생할 수 있다.
참여 완료를 하기 위해서는 MySQL을 거치는데, 만약 Kafka 특정 파티션의 오프셋 랙이 늘어난 경우에, 랙이 해소되기 전까지 푸시 서버에서 버킷에 기록하는 작업이 지연된다.
그 지연된 시간만큼 조인 스로틀링을 걸지 못하게 되어 MySQL에 그대로 요청이 가게 된다. 이는 MySQL에 더 큰 부하를 발생시키면서 지연 시간을 증가시키고, 결국 조인 스로틀링을 더더욱 지연시키게 된다.
이 문제를 해결하기 위해 오픈챗 참여 요청이 들어올 때 바로 참여 요청 수를 채팅별로 집계하는 방식을 사용할 수 있다.
이때 채팅별로 허용하는 참여 요청의 최대치를 제한하여 스로틀링을 적용할 수 있다.
역시 빅테크 기업의 테크블로그 내용은 너무 유익하다!! 🤩
메시지 처리, 부하 관리를 고려한 채팅 시스템 설계에 대해 알아보는 시간이었다.
또 mysql의 innodb_autoinc_lock_mode, kafka로 인해 발생할 수 있는 이슈 등 기술 이슈들도 알아갈 수 있었다. 기술 개념들이 많이 나오는데 하나씩 딥다이브 해도 재밌을 것 같다는 생각! (사실 요 블로그도 innodb_autoinc_lock_mode 알아보다가 마주친 글이었당 ㅎㅎ)
반응형'📖 개발 공부' 카테고리의 다른 글
유데미(Udemy) - Java 멀티스레딩, 병행성 및 성능 최적화 강의 후기 (0) 2024.03.31 JPA N+1 문제 해결 일지 - 답은 JPQL에 있었다. (0) 2024.03.03 Toss | null return은 코드 복잡성을 만든다. (0) 2024.02.17 빌드 관리 도구, 예제와 함께 Gradle 알아보기 (0) 2024.01.21 링크드리스트 구조로 정제 규칙 순서 관리하기 (1) 2023.12.20