개요
-기획한 콘서트 대기열 기능을 완성하고, k6를 통해 Load Test를 진행했습니다.
-위와 같이, Spring, Websocket 서버는 각각 이중화를 하고, Redis, Kafka는 별도의 인스턴스로 분리하였습니다.
그리고 Nginx로 로드밸런싱을 하고, 분산 환경에서 어플리케이션 모니터링을 위한 Pinpoint를 배포하였습니다.
- Load 테스트는 K6로 수행하고, 성능 테스트의 결과는 Prometheus에 적재하여 Grafana로 확인하였습니다.
1) Load Test 목표
- 콘서트 대기열 기능에서 사용자가 처음 페이지에 접근하면 WebSocket 연결이 이루어집니다.
본 Load Test의 목표는 동시 1만, 5만, 10만 명이 WebSocket을 통해 동시 접속하는 상황에서도
시스템이 안정적인 성능을 제공하는지 검증하는 것입니다.
- 이를 위해, 단순한 연결 테스트뿐만 아니라 STOMP 메시지의 송수신 과정도 검증하여,
메시지가 유실 없이 정상적으로 전달되는지 확인하는 것을 포함합니다.
특히, 다음과 같은 주요 항목을 점검합니다:
(1) WebSocket 연결 대기 시간 최소화: 동시 접속이 증가해도 서버가 빠른 시간 내에 연결을 하는지 확인
(2) WebSocket 연결 유지 여부: 동시 접속이 증가해도 서버가 정상적으로 연결을 유지하는지 확인
(3) STOMP 메시지 송수신 검증: token, waitingQueue, rank 등의 메시지가 정상적으로 전송 및 수신되는지 검증
(4) 메시지 유실 및 지연 측정: 높은 부하 상황에서도 메시지 유실 없이 빠르게 전달되는지 확인
- 본 테스트를 통해, 실제 운영 환경에서 WebSocket 기반 콘서트 대기열 시스템의 신뢰성을 확보하는 것을 목표로 합니다.
2) 1만 명 WebSocket 연결 및 메시지 송수신 부하 테스트
(1) 1만명 WebSocket 연결 테스트
- 웹소켓 연결에서 중요한 점은 사용자가 빠른 시간 내에 연결을 완료하는 것입니다.
따라서 이번 테스트의 목표는 P(95), 즉 95%의 사용자가 2초 이내에 연결을 완료하는 것,
그리고 1만명의 사용자가 웹소켓 세션을 3분동안 유지하는 것이었습니다.
(1-1) VUser 1만명 Grafana 대시보드
(1-2) VUser 1만명 k6 테스트 결과
- 1만 명 WebSocket 연결 테스트를 수행한 결과, 다음과 같은 문제점을 발견할 수 있었습니다.
(1) 사용자의 평균 웹소켓 유지 시간(ws_session_duration)이 2.4초를 기록했습니다.
이는 기대했던 3분에 비해 매우 짧은 수치로, 웹소켓 세션이 원활하게 유지되지 않고 있음을 시사합니다.
따라서 웹소켓 세션의 유지 시간이 충분히 확보될 수 있도록 서버 측 성능 최적화 및 로직 개선이 필요했습니다.
(1-3) 세션 연결 로깅
package com.example.concertTicket_websocket.websocket.infrastructure;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import java.util.logging.Logger;
public class LoggingWebSocketHandler extends WebSocketHandlerDecorator {
private static final Logger logger = Logger.getLogger(LoggingWebSocketHandler.class.getName());
// WebSocketHandler를 받아서 데코레이터로 처리
public LoggingWebSocketHandler(WebSocketHandler delegate) {
super(delegate);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 연결이 시작될 때 타임스탬프 기록
session.getAttributes().put("startTime", System.currentTimeMillis());
logger.info("WebSocket session started: " + session.getId());
super.afterConnectionEstablished(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
// 연결이 종료될 때 타임스탬프 기록
long startTime = (long) session.getAttributes().get("startTime");
long duration = System.currentTimeMillis() - startTime;
logger.info("WebSocket session closed: " + session.getId() + ", Duration: " + duration + " ms");
super.afterConnectionClosed(session, closeStatus);
}
}
- 실제로 세션 연결이 얼마나 유지되는지 확인해보기 위해서, Websocket 세션 로깅 핸들러를 추가했습니다.
- 그 결과 Websocket 세션이 기대한 시간인 3분에 못 미치는, 매우 짧은 시간(약 6초)에 끊어지는 것을 확인할 수
있었습니다
(1-3) JMX Exporter 설정 후 Heap Memory, GC 모니터링
- JVM 레벨에서 문제를 분석하고자, JMX Exporter를 설정한 후, Prometheus에서 스크래핑하여,
JVM의 Heap Memory, GC를 모니터링 했습니다.
- JVM GC를 모니터링한 결과, G1 GC의 Young 영역에서 Collection Time이 꾸준히 증가한 것을 확인할 수 있었습니다.
'콘서트 티켓 프로젝트' 카테고리의 다른 글
활성/비활성 사용자 구분 후 비활성 사용자 웹소켓 연결 끊김 처리 (0) | 2025.03.09 |
---|---|
Websocket과 Redis Pub/Sub을 활용한 대기열->활성화열 전환 알림 시스템 (0) | 2025.03.07 |
Nginx 로드밸런싱 설정하기 (0) | 2025.03.03 |