<시스템 디자인 연습> 채팅 서비스 2 - 기본 성능 테스트

작업

다대다 채팅을 실험했다. 같은 채팅방에 있는 유저 N명이 각자 s초 간격으로 메시지를 전송하는 상황을 가정했다.

목표

100명의 사용자가 1초 간격으로 각자 메시지를 입력할 때, 정상적인 입출력이 가능한 시스템을 구성한다. 커넥션 유지 시간을 10초로 두고 6회의 이터레이션을 돌게함으로써 약 1분간의 부하 테스트를 진행한다.

살펴봐야할 지표는

  • 커넥션 유지 - 주어진 시간 동안 100개 커넥션을 문제없이 유지하는지
  • RPS - 입력 RPS 100(100명 / 1초), 출력 RPS 10000(100명 * 100개 / 1초) 부하가 가능한지

결과 요약

  1. 커넥션을 유지하는 데에는 큰 부하가 없었다
  2. 사용자 수 제곱에 비례하는 출력수 때문에 부하가 크다.

환경

인프라: AWS ECS Fargate
서버: FastApi + Uvicorn
캐시 스토리지, 메시지큐: Redis
테스팅: K6 + Grafana + InfluxDB

결과 상세

작업1

N = 100, 요청 간격: 1초 -> 입력 RPS = 100, 출력 RPS = 10000  

한 개의 Fargate 태스크(vCPU 1개, 메모리 2GB)로 테스트를 시작했다. 실패율은 0%로 100개의 웹소켓 연결을 문제없이 유지했다. 어쩌면 웹소켓 커넥션을 유지하는 것 자체는 그리 큰 비용이 아닐 수도 있겠다고 판단했다.

하지만 입출력 메시지 일부가 누락되는 상황이 발견되었는데, 출력하는 메시지가 초당 5,690개에 그쳤기 때문이다. 예상했던, 초당 10,000개(100^2) 메시지를 받아야 했으나 57% 가량만 기록된 것이다. 입력에 성공한 메시지 기준으로 계산해보아도 70%를 밑돌았다. 총 출력 건의 기대 값은 574,100건(총 입력 5,741 건에 사용자수 100을 곱한 값)이나 395,696건으로 69% 수준에 불과했다.

[AWS ECS Fargate 설정]
태스크: 1
태스크 CPU: 1 vCPU
태스크 메모리: 2 GB

결과
checks…………….: 100.00%
ws_msgs_received……: 395696 5690.736973/s
ws_msgs_sent……….: 5741 82.564699/s
ws_session_duration…: avg=11.57s min=10.16s med=11.78s max=12.19s p(90)=12.11s p(95)=12.18s

작업2

N = 50, 요청 간격: 0.5초 -> 입력 RPS = 100, 출력 RPS = 5000

출력 RPS를 5,000으로 줄여 메시지 누락의 원인을 분석했다. 그랬더니 기대 출력 건 대비 97%(285,084 / (5,862 * 50)) 수준을 보여주는 것을 확인할 수 있었다. 출력 부하가 입력 부하보다 더 큰 영향을 주는 것으로 나타났다. 출력 수가 사용자 수 N의 제곱에 비례하기 때문이다.

결과
checks…………….: 100.00%
ws_msgs_received……: 285084 4626.873784/s
ws_msgs_sent……….: 5862 95.139447/s
ws_session_duration…: avg=10.24s min=10.03s med=10.23s max=10.44s p(90)=10.36s p(95)=10.37s

목표 부하 수준(출력 RPS 10,000)을 견디기 위해 태스크를 2개(메모리 4GB)로 확장했을 때, 입력 대비 약 96%의 출 력 메시지를 성공적으로 처리했다.

[AWS ECS Fargate 설정]
태스크: 2
태스크 CPU: 1 vCPU
태스크 메모리: 4 GB

[AWS Elasticache - redis 설정]
cache.t4g.small(메모리 1.37 GiB, 네트워크 5Gbps)

결과
checks…………….: 100.00%
ws_msgs_received……: 551920 8962.004184/s
ws_msgs_sent……….: 5756 93.465169/s
ws_session_duration…: avg=10.22s min=10.02s med=10.24s max=10.36s p(90)=10.31s p(95)=10.32s

그러나 추가적인 태스크 확장으로도 비슷한 부하 수준만 견뎌냈는데, 이는 Redis의 네트워크 처리량 병목 때문이었다. 메시지큐로 사용하는 Redis에서 허용된 네트워크 대역폭이 작업 수행 수준에 근접하였고, 실제로 대역폭 한계를 초과하는 이상치를 기록했다. 아래 이미지에서 좌측 그래프에서 NetworkBandwidthOutAllowanceExceed(주황색 선)를 확인할 수 있다. 이 병목 역시 입출력의 비대칭에서 기인하는데, 입력 메시지 한개당 사용자수 만큼의 출력 메시지를 내어야하는 상황에서, 출력 네트워크 처리량이 한계를 넘어선 것이다.

refs

Posts in this Series