개요
이번 글에서는 부하 테스트 결과를 분석하고, 이를 바탕으로 WAS 이중화를 통해 성능을 개선하는 방안을 살펴보겠다.
부하 테스트
이미 JMeter, Locust, K6 등 좋은 도구들이 많지만 내가 원하는 기능을 모두 충족하는 툴은 없었다. 그래서 Go 언어를 통해 직접 부하 테스트 툴을 작성했다.
시나리오
- 채팅방 입장 (Subscribe)
- 채팅 전송 (Send)
- 채팅 수신 완료 후 종료
테스트는 30초 동안 진행되며, 1000명, 3000명, 5000명이 동시에 접속하는 상황을 테스트한다.
인프라
CPU(Core) | RAM(GB) | |
WAS | 2 | 2 |
DB | 2 | 4 |
테스트 결과
테스트 결과, 1,000명의 동시 접속자인 경우 평균 1.2초의 지연 시간이 발생하며, 심하 경우 최대 4초에 가까운 지연이 확인되었다.
3,000명부터는 평균 18초의 지연시간이 발생으며 메시지를 완벽하게 처리하지 못하는 경우도 발생하였다..
하나의 WAS가 모든 트래픽을 처리하는 구조와 내장 브로커 사용으로 인해 리소스 부족이 발생했기 때문으로 보인다.
WAS 이중화
Docker Compose를 이용해 Nginx를 통해 SpringBoot 서버 이중화를 진행하려 한다.
docker-compose.yaml
services:
spring-10k-chat-server1:
image: spring-10k-chat-server
container_name: spring-10k-chat-server1
restart: always
volumes:
- ./jar:/opt/app
- ./jar/dump:/dump
networks:
- spring-10k-chat-network
environment:
PROFILE: test
deploy:
resources:
limits:
cpus: '2'
memory: '2048M'
spring-10k-chat-server2:
image: spring-10k-chat-server
container_name: spring-10k-chat-server2
restart: always
volumes:
- ./jar:/opt/app
- ./jar/dump:/dump
networks:
- spring-10k-chat-network
environment:
PROFILE: test
deploy:
resources:
limits:
cpus: '2'
memory: '2048M'
nginx:
image: nginx:latest
container_name: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
environment:
TZ: "Asia/Seoul"
networks:
- spring-10k-chat-network
restart: always
networks:
spring-10k-chat-network:
external: true
nginx.conf
worker_processes 1;
events {
worker_connections 4096;
# worker_processes and worker_connections allows you to calculate maxclients value:
# max_clients = worker_processes * worker_connections
}
http {
upstream spring-10k-chat-server {
least_conn;
server spring-10k-chat-server1:8080 max_fails=3 fail_timeout=30s;
server spring-10k-chat-server2:8080 max_fails=3 fail_timeout=30s;
}
client_max_body_size 10M;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://spring-10k-chat-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE 설정
proxy_read_timeout 3600s;
proxy_buffering off;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With';
add_header 'Access-Control-Allow-Credentials' 'true';
return 204;
}
}
# STOMP 설정
location /ws-stomp {
proxy_pass http://spring-10k-chat-server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
}
개선 후 부하테스트
인프라
CPU(Core) | RAM(GB) | |
WAS1 | 2 | 2 |
WAS2 | 2 | 2 |
DB | 2 | 4 |
테스트 결과
개선 후 테스트 결과 1,000명의 동시 접속자가 발생한 경우 약간의 지연이 더 발생했다. 아마 Nginx를 통해 처리되므로 그에 따른 약간의 지연 시간이 있을 것이고, WAS의 처리량이 늘어 DB에 부하가 심해져 지연이 더 발생한 것으로 예상된다. 그래도 3,000명의 경우에는 평균 약 6초정도 빨라졌다.
서버의 이중화가 필요한 이유
테스트 결과를 보았듯이 서버를 이중화해도 큰 이득을 보지 못했다. 그럼에도 불구하고 서버를 이중화해야하는 이유에 대해 살펴보자.
1. 장애 대응
- 하나의 서버가 장애로 다운되더라도 다른 서버가 요청을 계속 처리해 서비스 연속성이 보장된다.
- 단일 서버로 운영하는 경우 서버 다운 시 전체 서비스가 중단되는 단일 실패 지점(Single Point of Failure) 문제가 발생할 수 있다.
2. 트래픽 분산
- 서버가 여러 대일 경우 로드 밸런서가 요청을 여러 서버로 분산 처리해 동시 접속자 수가 많아도 안정적으로 서비스를 제공할 수 있다.
3. 확장성
- 서비스가 성장하면서 트래픽이 증가할 때 Scale-Out을 통해 수평적으로 확장이 가능해 대규모 서비스에도 대응할 수 있다.
Scale Out VS Scale Up
Scale Out (수평 확장)
서버 또는 인프라 개수를 늘리는 방식이다.
여러 대의 서버를 연결하여 트래픽을 분산 처리한다.
장점
- 무중단 확장이 가능
- 장애 대응이 용이 (특정 서버 다운 시 다른 서버가 처리)
- 클라우트 환경에 적합 (Auto Scaling)
단점
- 구성 복잡도 증가 (로드 밸런싱, 세션 관리 필요 등)
Scale Up (수직 확장)
기존 서버의 성능을 강화하는 방식이다.
CPU, 메모리, 스토리지 등의 하드웨어 스펙을 업그레이드한다.
장점
- 기존 인프라 재사용 가능
- 관리가 상대적으로 쉬움
단점
- 업그레이드 한계가 존재 (하드웨어 한계)
- 고사양 서버 비용 증가
결론
이번 장에서는 부하 테스트를 진행하고 성능 개선을 위해 WAS 이중화를 구성했다.
비록 눈에 띄는 성능 향상을 이루지 못했지만, 단일 장애 지점을 방지하여 서비스 안정성을 확보했다는 점에 의미를 둬야겠다.
- STOMP 부하 테스트 코드 링크 : https://github.com/woong99/stomp-load-test-tool
- 백엔드 코드 : https://github.com/woong99/spring-chat
- 프론트엔드 코드 : https://github.com/woong99/spring-chat-front
'Programming > SpringBoot' 카테고리의 다른 글
[SpringBoot] AMQP란? (0) | 2025.01.26 |
---|---|
[10k-Chat] Spring Boot 실시간 채팅 서버 구현 (1) - Stomp (0) | 2025.01.19 |
[SpringBoot] JPA 동적 스키마 (1) | 2024.11.06 |
[SpringBoot] @ModelAttribute 작동 원리 (0) | 2024.07.17 |
[SpringBoot] Entity의 ID를 테스트에서 삽입하는 방법 (0) | 2024.06.02 |