Tech-Verse 2022에서 김인제 님이 발표한 LINE 오픈챗 서버가 100배 급증하는 트래픽을 다루는 방법 세션 내용을 옮긴 글입니다.
안녕하세요. Square Dev 팀 김인제입니다. 이번 글에서는 LINE 오픈챗(OpenChat) 서버에서 100배 급증하는 트래픽을 다루는 방법을 소개하겠습니다.
아래는 이번 글에서 공유하고자 하는 핵심 내용을 슬라이드 한 장으로 요약한 것입니다. SNS 팔로워 수가 수백만 명인 유명한 가수의 콘서트를 팬들이 함께 보며 실시간으로 오픈챗에서 활발하게 메시지를 주고받았던 사례입니다.
수천 명의 사용자가 한 오픈챗 안에서 많은 메시지를 동시에 주고받다 보면 일반 오픈챗 대비 100배 이상 트래픽이 급증할 수 있습니다. 오픈챗 서버 팀에서는 이와 같은 챗을 '핫 챗'이라고 부르는데요. 이번 글에서는 오픈챗 서버 팀이 경험한 두 가지 핫 챗 패턴과 핫 챗 때문에 발생한 문제, 그 문제를 어떤 방식으로 해결했는지 공유하겠습니다. 글은 LINE 오픈챗 소개, 오픈챗 서버 팀이 핫 챗에서 급증하는 트래픽을 다루는 방법, 향후 계획 순으로 진행합니다.
LINE 오픈챗 소개
먼저 간단히 LINE 오픈챗을 소개하겠습니다. LINE 오픈챗은 LINE 앱 대화 탭 우측 상단에 있는 네모 모양의 오픈챗 로고를 클릭해 사용할 수 있습니다. 다양한 오픈챗을 추천받거나 검색할 수 있으며, 오픈챗에 들어가면 메시지나 이미지, 메시지 리액선 등을 실시간으로 주고받을 수 있습니다.
오픈챗 서버는 1분에 천만 개, 하루에 약 100억 개의 API 요청을 처리하고 있습니다. 한 오픈챗에 수천 명부터 수만 명의 사용자가 참여할 수 있고, 활발한 오픈챗은 한 오픈챗에서만 1분에 20만 개의 API 요청을 처리하기도 합니다.
만약 한 오픈챗에 참여한 5천 명의 사용자가 모두 동시에 활발하게 메시지를 주고받는다면 어떤 일이 발생할까요? 실제로 소셜미디어 팔로워가 수백만 명에 달하는 유명 가수의 콘서트를 팬들이 함께 보며 참여하는 오픈챗이 있었고, 배우나 모델, 인플루언서의 방송 출연을 함께 보는 오픈챗, 온라인 게임의 이벤트를 함께 즐기는 오픈챗 등이 있었습니다.
이와 같은 오픈챗에선 일반적인 오픈챗 대비 트래픽이 100배 이상 급증할 수 있습니다. 아래 오른쪽 사진을 보면 5천 명이 참여해 1초에 수백 개의 메시지가 전송될 정도로 활발하게 활동하는 한 오픈챗이 나타나서, 이 챗의 새로운 메시지들을 가져가려는 API 요청 수가 1분에 최대 20만 건에 달하며 급증하는 것을 확인할 수 있습니다. 이와 같이 트래픽이 급증하는 오픈챗을 오픈챗 서버 팀에서는 핫 챗이라고 부릅니다.
핫 챗에서 이렇게 많은 수의 API 요청이 발생하는 이유는 오픈챗 서버의 이벤트 기반 아키텍처와 깊은 관련이 있습니다. 오픈챗 서버에서는 메시지 전송과 메시지 리액션, 메시지 읽음 등과 같은 오픈챗 내 다양한 행위를 모두 이벤트로 간주하고 이벤트가 생성될 때마다 스토리지에 저장한 후 오픈챗에 참여하고 있는 모든 사용자에게 서버 푸시로 '새로운 이벤트가 생성됐으니 받아 가세요'라고 알립니다. 서버 푸시를 받은 사용자(클라이언트)는 스토리지에 새로 들어온 이벤트를 페치(fetch) 이벤트 API를 이용해 받아 가서 새 메시지 등을 화면에 추가하는 액션을 실행합니다.
이와 같은 이벤트 기반 아키텍처에서는 이벤트가 생성될 때마다 오픈챗에 참여하고 있는 모든 사용자에게 전달해야 합니다. 5천 명이 한 오픈챗에 참여하고 있다고 가정하면 이벤트가 하나 생성될 때마다 5천 명의 사용자가 이 이벤트를 전달받기 위해 5천 개의 페치 이벤트 API 요청을 호출하는 것입니다. 따라서 1초에 수십 개의 이벤트가 생성되는 핫 챗에서는 그만큼 페치 이벤트 API 요청이 급증하게 됩니다.
핫 챗에서 급증하는 트래픽을 다루는 방법
이와 같이 트래픽이 급증하는 핫 챗은 오픈챗 서비스가 성장하고 있다는 지표임과 동시에 오픈챗 서버가 해결해 할 도전 과제이기도 합니다. 핫 챗 때문에 오픈챗 서버에서 실제로 발생했던 문제와 해결해 나간 과정을 핫 챗 패턴별로 설명하겠습니다.
패턴 1: 페치 이벤트 API 요청 급증
첫 번째로 소개할 핫 챗 패턴은 사용자가 새로운 이벤트들을 받아기 위한 페치 이벤트 API 요청이 급증하는 핫 챗입니다.
문제 현상
아래는 오픈챗 서버 구조를 조금 더 자세히 그려본 다이어그램입니다. 메시지를 전송하면 오픈챗 서버가 이를 스토리지에 저장하고 Kafka 이벤트를 전달하며, 오픈챗 서버 팀에서 '퍼블리시(publish) 서버'라고 부르는 별도 서버 그룹에서 Kafka 이벤트를 소비해서 새로운 이벤트가 생성됐다고 알리는 서버 푸시를 사용자에게 보냅니다.
이런 구조에서 5천 명의 사용자가 1초에 수십 개의 메시지를 주고받는 핫 챗에서는 페치 이벤트 API 요청 수가 평소 대비 100배 이상 급증하면서 특히 오픈챗 서버에서 사용하는 스토리지에 큰 부하가 발생하는 문제가 있었습니다.
아래는 관련 지표입니다. 왼쪽 사진을 보면 한 핫 챗에서 페치 이벤트 API 요청이 급증하면서 MySQL과 Redis 등의 스토리지에서 한 샤드(shard)에 전달되는 요청량이 3배 이상 급증하거나 스토리지 부하 때문에 오픈챗 서버에서 응답 타임아웃이 발생하는 경우가 발생했습니다.
또한 이 핫 챗의 이벤트를 처리하는 Kafka의 한 파티션에 대량 이벤트가 생성돼 오프셋 랙(offset lag)이 증가하거나 핫 챗 요청을 처리하는 서버 그룹의 GC(garbage collect)와 CPU 사용량이 급증하는 것을 지표로 확인할 수 있었습니다.
그렇다면 왜 핫 챗은 스토리지에 이런 큰 부하를 발생시키는 걸까요? 오픈챗 서버에서는 데이터를 저장하기 위해 MySQL과 Redis, HBASE, Kafka 등 다양한 스토리지를 사용하고 있으며, 챗 ID를 기반으로 샤딩(sharding)해서 데이터를 저장합니다. 오픈챗 서비스가 성장해 더 많은 챗이 생성되면 샤드를 추가하는 방식으로 확장할 수 있는 구조이지만, 핫 챗은 하나의 오픈챗이므로 챗 ID를 기반으로 샤딩하는 구조에서는 하나의 핫 챗 안에서 발생하는 데이터를 더 이상 분산시킬 수 없습니다. 따라서 하나의 핫 챗에서 발생하는 모든 요청은 스토리지에서 하나의 샤드, 하나의 키로 몰리면서 부하가 집중됩니다.
해결 방안
한 샤드로 집중되는 핫 챗의 부하를 줄이기 위해서는 샤드를 추가해 전체 샤드 수를 늘리거나 샤드의 복제본 수를 늘리는 등의 방법을 고려해 볼 수 있습니다.
하지만 핫 챗이 전체 오픈챗에서 차지하는 비중은 0.1% 미만에 불과합니다. 언제 발생할지 모르는 극소수의 핫 챗을 위해 샤드를 추가하거나 전체 데이터의 애플리케이션 수를 늘리는 등의 방법은 오버헤드가 너무 크다고 판단했습니다.
딱 핫 챗만 타깃으로 적용할 수 있는 해결 방법이 없을까 고민했고, 그 결과 실시간으로 핫 챗을 탐지해서 핫 챗에서 발생하는 페치 이벤트 API 요청만 줄이는 방법을 택했습니다. 이 방법으로 핫 챗 때문에 하나의 샤드에 몰리는 부하만을 효과적으로 줄일 수 있다고 판단했습니다. 이 방식을 핫 챗 감지 및 스로틀링(detection & throttling)이라고 부르고 있으며, 어떻게 작동하는지 조금 더 자세히 설명하겠습니다.
우선 실시간으로 어떤 챗이 핫 챗인지 탐지할 수 있는 방법이 필요했고, Kafka와 버킷(bucket)으로 구현했습니다. 페치 이벤트 API가 요청될 때마다 Kafka로 이벤트를 전송하고 퍼블리시 서버에서 이를 소비해 하나의 챗에 최근 몇 초 동안 몇 개의 페치 이벤트 API가 요청됐는지 실시간으로 기록합니다. 만약 페치 이벤트 API 요청이 미리 설정해 둔 임곗값(스토리지에 큰 부하를 주기 시작하는 요청 수)보다 더 많이 들어온다면 이를 핫 챗으로 판단, 해당 챗이 핫 챗이라고 Redis에 잠시 저장합니다.
이 과정을 코드로 살펴보면, 챗 ID별로 최근 몇 초 동안 몇 개의 페치 이벤트 API 요청이 들어왔는지를 기록할 수 있는 버킷을 준비하고, 페치 이벤트 API 요청마다 이 버킷에 기록하다가 임곗값을 넘으면 Redis에 해당 챗이 핫 챗임을 잠시 저장합니다.
퍼블리시 서버에서는 사용자에게 새로운 이벤트가 생성됐다고 알리는 서버 푸시를 보내기 전에 이 이벤트가 생성된 챗이 핫 챗인지 Redis에서 확인합니다. 만약 핫 챗이면 서버 푸시를 확률적으로 스로틀링해서 보내지 않는 방법으로 대량의 페치 이벤트 API 요청이 발생하지 않도록 조절합니다. 이 방법으로 핫 챗 때문에 발생하는 스토리지 부하만 타깃으로 잡고 줄일 수 있었습니다.
핫 챗 스로틀링이 없던 과거에는 아래 왼쪽 그림처럼 1초에 수십 개의 이벤트가 한 챗에서 생성되면 이벤트 생성마다 5천 개의 서버 푸시를 전송했고, 사용자는 5천 개의 페치 이벤트 API 요청을 호출했습니다. 하지만 핫 챗 스로틀링을 적용한 후에는 오른쪽 그림처럼 핫 챗을 대상으로만 서버 푸시 개수를 조절할 수 있게 됐습니다. 사용자 입장에서는 하나의 서버 푸시만 받으면 새롭게 생성된 이벤트를 모두 받아 갈 수 있기 때문에, 1초에 수십 개의 이벤트가 발생하더라도 사용자에게 거의 영향을 주지 않고 핫 챗의 페치 이벤트 API 요청량만 효과적으로 줄일 수 있습니다.
핫 챗을 탐지하는 기준치나 핫 챗일 때 서버 푸시 스로틀링을 몇 초 간 어느 정도로 적용할지, 특정 챗에 스로틀링을 적용할지 등은 모두 LINE의 오픈 소스인 LINE Central Dogma만으로 서버 재기동 없이 동적으로 변경할 수 있도록 구현했습니다. 따라서 갑자기 발생한 핫 챗에서 큰 부하가 발생해도 오픈챗 서버 팀에서 신속하게 대응할 수 있습니다.
이전에는 아래 상단 사진처럼 핫 챗에서 부하가 급증하면 스토리지의 샤드 하나로 요청이 몰려 느린 쿼리(slow query)나 응답 타임아웃이 발생하는 경우가 종종 있었는데요. 핫 챗 감지 및 스로틀링을 적용한 뒤로는 핫 챗을 자동으로 탐지한 뒤 핫 챗에만 스로틀링을 적용하므로 핫 챗으로 인한 스토리지 부하 등의 이슈가 더 이상 발생하지 않고 있습니다.
또한 어떤 챗이 핫 챗인지, API 요청이 어느 정도 발생하고 있는지 실시간으로 확인할 수 있는 핫 챗 대시보드를 만들었습니다. 이 대시보드를 이용해 오픈챗 서버 팀에서 핫 챗을 모니터링하면서 신속하게 대응하고 있습니다.
패턴 2: 오픈챗 참여 요청 급증
두 번째로 소개할 핫 챗 패턴은 오픈챗 참여 요청이 급증하는 핫 챗입니다.
문제 현상
LINE 오픈챗에서는 직접 오픈챗을 검색하거나 추천 오픈챗을 통해 오픈챗에 참여할 수 있고, 오픈챗 참여 QR 코드나 링크 공유를 통해서도 참여할 수 있습니다.
2021년까지만 해도 한 오픈챗에 짧은 시간 동안 오픈챗 참여가 급격하게 몰리는 경우는 거의 없었는데요. 2022년부터는 최대 1초에 2천 개의 오픈챗 참여 요청이 한 오픈챗에 몰리는 경우가 발생했습니다. 소셜미디어 팔로워를 수백만 명 보유하고 있는 인플루언서 분들이 자신의 오픈챗 참여 QR 코드를 소셜미디어에 업로드하는 등 오픈챗 서비스가 성장하며 점점 더 많은 곳에서 다양한 방법으로 활용되고 있기 때문이었습니다.
오픈챗 서버는 오픈챗 참여 요청이 오면 챗 멤버 데이터를 MySQL에 저장하는데요. 오픈챗 참여 요청이 한 챗에 몰리자 특히 MySQL 부하가 급증하는 것을 확인할 수 있었습니다.
실제로 1초에 2천 개 이상의 오픈챗 참여 요청이 한 챗에서 발생했을 때의 지표입니다. MySQL 1개 샤드에 INSERT 쿼리가 순간적으로 몰리고 느린 쿼리와 CPU 사용량이 급증하는 것을 확인할 수 있었습니다.
이에 따라 오픈챗 서버에서는 응답 타임아웃이 발생하고, MySQL의 한 샤드로 전달되는 요청들의 처리가 지연되는 것을 확인할 수 있었습니다.
해결 방안 1 - MySQL 병목 지점 제거
이 문제를 해결하기 위해 우선 MySQL의 병목 지점들을 찾아서 해결했습니다.
첫 번째로 찾은 병목 지점은 바로 '챗 멤버 INSERT
쿼리'였습니다. MySQL에서는 오픈챗 참여 요청이 오면 해당 쿼리를 실행하는데요. 이때 이미 가입된 사용자인지, 중복된 멤버 이름인지를 검사하기 위해 아래 왼쪽 사진처럼 INSERT
쿼리 내부에서 다시 SELECT
쿼리를 사용하는 서브 쿼리를 사용하고 있었습니다.
이와 같은 서브 쿼리는 간단한 INSERT
쿼리와는 달리 MySQL에서 벌크 INSERT
구문으로 취급합니다. 이때 MySQL의 innodb_autoinc_lock_mode
가 기본값인 1이라면 벌크 INSERT
구문은 AUTO-INCREMENT
값을 증가시키기 위해 테이블 락을 잡습니다. 오픈챗 서버에서는 MySQL의 AUTO-INCREMENT
락(lock) 모델을 기본값(1)으로 사용하고 있었기 때문에 한 오픈챗에 참여 요청이 급증하면 AUTO_INCREMENT
테이블 락 경합도 같이 급증했습니다.
실제로 한 오픈챗에 참여 요청이 몰렸을 때의 MySQL 지표를 확인해 보면 AUTO_INCREMENT
테이블 락을 잡기 위한 경합이 굉장히 많았고 이 때문에 MySQL의 CPU 사용률이 100%에 도달해 응답 타임아웃이 발생하는 것을 확인할 수 있었습니다.
이와 같은 AUTO_INCREMENT
테이블 락 경합을 줄이기 위해 테이블 락을 잡지 않도록 MySQL의 AUTO-INCREMENT
락 모드를 기본값인 1에서 2(interleaved
모드)로 변경했습니다. 변경한 모드에서는 동시에 여러 개의 값을 INSERT
할 때 AUTO-INCREMENT
값이 연속적이지 않을 수 있는데요. 사용하고 있는 쿼리에서는 한 번에 한 명만 INSERT
하고, 또 AUTO-INCREMENT
값이 연속된다고 가정한 로직이 없어서 AUTO-INCREMENT
락 모드를 변경해도 괜찮다고 판단했습니다.
그 결과 한 오픈챗에 1초에 수천 개의 참여 요청이 들어와도 MySQL은 CPU 사용률을 10~20% 사이를 유지하며 안정적으로 처리했습니다.
두 번째로 오픈챗에 참여하고 있는 멤버 수를 가져오는 쿼리에서도 병목 지점을 찾을 수 있었습니다. 해당 쿼리는 state = JOINED
인 멤버 수를 집계하는 방식이었는데요. 이전에는 오픈챗에 참여가 몰리는 일이 거의 없어서 성능을 높이기 위해 오픈챗 멤버를 MySQL 쿼리 캐시를 이용해 캐싱해서 사용하고 있었습니다.
하지만 1초에 2천 개 이상의 오픈챗 참여 요청이 몰리자 문제가 발생했습니다. 참여하고 있는 멤버 수를 캐싱하고 있는 MySQL 쿼리 캐시가 참여 요청 하나를 처리할 때마다 멤버 수 값을 갱신했는데요. 이때 테이블 락을 잡고 멤버 수를 재계산해 쿼리 캐시를 갱신하면서 큰 부하가 발생했습니다.
아래는 오픈챗 참여 요청이 몰리자 쿼리 캐시 갱신 때문에 테이블 락 경합이 높아지는 현상을 지표를 통해 확인한 것입니다.
이 문제는 참여하고 있는 멤버 수 값을 집계하는 별도 테이블을 도입하고 MySQL 쿼리 캐시는 제거하는 것으로 해결할 수 있었습니다.
이런 병목 지점들은 오픈챗 참여 요청이 많이 몰리지 않던 2021년까지는 불거지지 않다가 2022년에 오픈챗 참여 요청이 급격히 몰리면서 수면 위로 드러난 문제점들이었습니다.
해결 방안 2 - 조인 스로틀링 적용
MySQL 병목 지점을 조사하는 중에도 오픈챗 참여 요청이 몰리는 핫 챗들이 지속적으로 발생했습니다. 이에 MySQL 병목 지점 조사를 하면서 동시에 오픈챗 참여 요청이 MySQL 처리 한계를 넘지 않도록 빠르게 조인 스로틀링을 적용했습니다.
조인 스로틀링은 핫 챗 스로틀링과 비슷하게 오픈챗 참여 요청 처리를 완료할 때마다 Kafka로 이벤트를 전송해 퍼블리시 서버에서 각 챗 별로 몇 개의 조회 요청이 들어오고 있는지 버킷에 기록합니다. 기록한 값이 설정해 둔 MySQL 처리량 한계를 넘으면 '잠시 뒤 다시 참여 요청해 주세요'라는 팝업 메시지를 반환하도록 구현했습니다. MySQL 병목 지점 해결 작업을 아직 진행하고 있던 시점이었기 때문에 몰리는 참여 요청으로 MySQL이 사용 불능이 되는 것보다는 참여 요청이 몰린 핫 챗에만 잠시 스로틀링을 걸어서 다른 챗에 미치는 영향을 줄인 것입니다.
조인 스로틀링을 적용하면서 얻은 교훈이 있습니다. Kafka를 사용하는 조인 스로틀링은 항상 몇 초 정도의 지연이 발생할 수 있다는 점이었습니다. 참여 요청 완료 후 Kafka를 통해 퍼블리시 서버로 참여 완료 이벤트를 전달하려면 Redis와 MySQL, Kafka 등 거쳐야 하는 스토리지가 많습니다 이때 특히 Kafka는 핫 챗 때문에 특정 파티션의 오프셋 랙이 순간적으로 증가해 몇 초 정도의 처리 지연이 발생할 수 있습니다. 즉 퍼블리시 서버 앞단에서 지연이 발생하면 조인 스로틀링도 그만큼 지연되는 구조인 것입니다.
만약 1초에 수천 개의 오픈챗 참여 요청이 한 챗에 몰리고 있는 상황에서 조인 스로틀링이 몇 초 지연되면 어떻게 될까요? 1초 동안 몰린 수천 개의 오픈챗 참여 요청이 조인 스로틀링이 지연된 시간만큼 정상적으로 스로틀링되지 않고 모두 MySQL로 넘어갑니다. 이렇게 조인 스로틀링이 지연돼 MySQL로 넘어간 요청들은 MySQL에 더 큰 부하를 발생시키면서 지연 시간을 증가시키고, 결국 조인 스로틀링을 더욱더 지연시킵니다.
실제로 조인 스로틀링이 잘 작동하다가 몇 초 정도 지연된 적이 있었는데요. 하필 그때 1초에 수천 개의 요청이 몰리면서 MySQL로 모든 요청이 넘어가 큰 부하를 발생시키면서 조인 스로틀링을 더욱더 지연시켜서 결국 모든 참여 요청이 허용되며 MySQL의 처리 한계를 넘겼고, 응답 타임아웃이 발생한 적이 있습니다.
이 문제를 해결하기 위해 조인 스로틀링에 Kafka가 아닌 Redis를 사용해서 오픈챗 참여 요청이 들어올 때마다 바로 Redis에서 참여 요청 수를 챗별로 집계하는 방식을 사용할 수도 있는데요. 핫 챗은 0.1% 비율로 발생하는 극소수의 챗인데 이를 위해 99.9%에 해당하는 일반적인 챗의 모든 참여 요청까지 Redis에 함께 기록하는 것은 큰 오버헤드라고 생각했습니다. 이에 Redis가 아닌 로컬 캐시를 사용해 챗별로 허용하는 참여 요청의 최대치를 제한하는 방식을 적용했습니다.
해결 방안 3 - 서킷 브레이커와 벌크헤드 도입
핫 챗 때문에 스토리지의 한 샤드의 처리 속도가 느려지거나 사용 불능이 되더라도 다른 샤드로 가는 요청은 영향받지 않고 처리되도록 서킷 브레이커와 벌크헤드(bulkhead)를 도입했습니다.
서킷 브레이커는 스토리지의 샤드별로 응답 타임아웃과 같은 에러가 많이 발생하면 요청들을 빠르게 실패로 처리합니다. 벌크헤드는 하나의 샤드로 몰린 요청들이 스레드 풀을 독점하지 않도록 막아줍니다. 이를 통해 한 샤드에 요청이 몰릴 때 다른 샤드에 영향을 주지 않도록 부하를 격리할 수 있습니다.
서킷 브레이커와 벌크헤드를 도입한 결과 오픈챗 참여 요청이 하나의 챗에 1초에 수천 개가 몰려도 문제없이 안정적으로 처리할 수 있게 개선할 수 있었습니다.
문제 해결 과정에서 배운 점
여기까지 그동안 경험했던 두 가지 핫 챗 패턴과 각 패턴에서 발생한 문제 및 해결 방식을 설명했습니다. 이 과정에서 오픈챗 서버 팀이 배운 교훈은 다음과 같습니다.
첫 번째로 핫 챗을 이해할 필요가 있다는 것입니다. 오픈챗 서비스가 성장하면서 다양한 패턴의 핫 챗이 새롭게 발생했으며, 이 핫 챗들이 스토리지에 큰 부하를 줄 수 있다는 것을 알게 됐습니다. 또한 핫 챗은 그 패턴이나 발생 시점을 미리 예측해서 대응하기 어렵다는 사실도 알게 됐습니다.
두 번째로 핫 챗을 찾고 병목 지점을 해결하기 위해서는 API 단위의 요청량 모니터링뿐 아니라 국가와 애플리케이션 종류나 챗별 요청량까지 모니터링할 수 있어야 한다는 것입니다. API 단위 요청량을 모니터링하는 것만으로는 급증하는 부하가 핫 챗 때문에 발생한 부하인지, 어떤 챗이 핫 챗인지를 파악할 수 없었습니다.
세 번째로 병목 지점을 찾는 과정에서도 지속해서 핫 챗이 발생할 수 있으므로 스토리지 처리량 한계를 넘지 않도록 스로틀링을 먼저 빠르게 적용한 뒤 병목 지점을 찾아 해결해 나가는 방식이 효과적이라는 것을 알게 됐습니다. 이때 메모리에서 작동하는 로컬 캐시와 서버 재시작 없이 설정값을 변경할 수 있는 동적 설정이 갑자기 발생한 핫 챗 부하에 신속하고 유연하게 대응하는 데 큰 도움이 됐습니다.
마지막으로 핫 챗 때문에 하나의 샤드에 몰리는 부하를 잘 격리하기 위해서는 스토리지 샤딩뿐 아니라 샤드별로 서킷 브레이커와 벌크헤드까지 필요하다는 것을 알게 됐습니다. 또한 핫 챗은 0.1% 미만의 극소수 챗이며 매일 발생하는 것도 아니기 때문에 해결책을 생각할 때 핫 챗만 타깃으로 삼아 적용할 수 있는 적절한 방법을 고려해야 한다는 것도 알게 됐습니다.
마치며
이번 글을 슬라이드 한 장으로 요약해 보겠습니다.
오픈챗 서버 팀은 페치 이벤트 API 요청과 오픈챗 참여 요청이 급증하는 패턴을 경험했고, 각 문제와 병목 지점을 핫 챗 감지 및 스로틀링과 MySQL 성능 개선, 조인 스로틀링을 통해서 해결할 수 있었습니다. 이 과정에서 핫 챗 때문에 발생한 영향이 다른 일반 오픈챗으로 번지는 것을 막기 위해 서킷 브레이커와 벌크헤드를 도입했고, 해결책을 고를 때에는 핫 챗이 전체 오픈챗 중 0.1% 미만이라는 사실을 고려해 선정했습니다. 결국 극소수 핫 챗 때문에 서비스에 발생한 영향을 격리하고 최소화하는 것을 목표로 한 것입니다.
마지막으로 앞으로 오픈챗 서버가 어느 방향으로 나아가려고 하는지 말씀드리겠습니다.
먼저 핫 챗이 발생했을 때 핫 챗만 저장하는 별도 스토리지로 핫 챗 데이터를 자동으로 옮기거나 핫 챗 부하를 더 잘 분산시킬 수 있는 방법을 찾아서 적용해 나갈 예정입니다. 또한 핫 챗을 판단하는 기준을 고정값이 아닌 핫 챗의 최근 부하에 따라 변화하는 값으로 적용하고, 핫 챗 기준도 핫 챗이 활발한 정도에 따라 여러 개를 도입해 더 활발한 핫 챗에는 더 높은 스로틀링을 적용하는 등의 개선안도 도입하고자 합니다.
오픈챗 서비스는 꾸준히 성장하고 있습니다. 이에 따라 더욱 다양한 기능을 추가할 예정이고, 더 많은 트래픽이 몰릴 것으로 예상합니다. 아직 경험하지 못한 더 다양한 패턴의 핫 챗이 발생할 것이라고 예상하는데요. 어떤 핫 챗에도 유연하게 대응할 수 있도록 오픈챗 서버 아키텍처를 개선할 예정이며, 결과적으로 서비스 성장을 뒷받침할 수 있는 신뢰할 수 있는 오픈챗 서버로 나아가고자 합니다. 긴 글 읽어주셔서 감사합니다.