예상 트래픽 기준: DAU 100K, Peak CCU 30K, Peak 1.5K TPS
인프라 설계: 비즈니스 로직 및 DB 처리용 r6i.large (2 Core, 16GB RAM), 알림 로직 처리용 m7g.medium (1 Core, 4GB RAM) x 2.
flowchart LR
Client["Client"] -->|SSE Keep-Alive| App["Notification Server"]
App --> Emitter["SseEmitter"]
Emitter --> Socket["TCP Socket"]
Socket --> Kernel["OS Kernel<br/>tcp_rmem / tcp_wmem"]
App --> Serialize["@JsonSerialize<br/>응답 최소화"]
- C20K 동시 SSE 커넥션 환경에서 힙·네이티브 메모리가 선형 증가하며 OOM이 발생함.
- OS 기본 소켓 버퍼(
tcp_rmem/tcp_wmem≈ 128KB)가 커넥션당 과도하게 할당되어 메모리 상한이 빨리 소진됨. - Spring
SseEmitter기본 설정과 과도한 JSON 페이로드가 커넥션당 응답·버퍼 비용을 키움.
- Linux
tcp_rmem/tcp_wmem을 SSE Keep-Alive 트래픽 특성에 맞게 하향 튜닝해 커넥션당 커널 버퍼 점유를 줄임. - 알림 DTO에
@JsonSerialize기반 선언적 직렬화를 적용해 필드·중첩 객체를 최소화한 경량 이벤트로 송출함. SseEmitter타임아웃·재연결(Last-Event-ID) 정책을 명시해 유휴 커넥션 누수와 재전송 비용을 통제함.- Emitter 저장소를 멤버 단위
ConcurrentHashMap으로 구성해 브로드캐스트 시 O(1) 조회·정리 경로를 확보함.
- 커넥션당 메모리 사용량 약 40KB 감소.
- 동일 리소스 기준 최대 동시 SSE 커넥션 수 3.1배 증가.
- C20K 부하 테스트에서 OOM 0건, 안정적인 Keep-Alive 유지율 확보.
flowchart TB
Clients["Clients<br/>C10K Traffic"] --> Tomcat["Tomcat<br/>max-connections"]
Tomcat --> Threads["Worker Threads"]
Threads --> App["Order / Product API"]
App --> Pool["HikariCP<br/>maximumPoolSize"]
Pool --> DB["MySQL"]
- C10K 트래픽 급증 시 P99 응답이 timeout 상한(약 30s)까지 밀리며 사용자 체감 지연이 커짐.
- Tomcat 기본
max-connections(8192)이 동시 수용량을 넘지 못해 요청이 대기열에 적체됨. - HikariCP 풀 경합으로 DB 커넥션 대기 Pending thread가 폭증하고
dial tcp: i/o timeout이 발생함.
- Tomcat
max-connections를 15000으로 상향해 급증 트래픽의 동시 수용 한계를 확장함. - HikariCP
maximumPoolSize를 CPU 코어 수 기반 공식으로 재산정해 풀 과다·과소로 인한 경합을 줄임. - 주문·상품 API의 트랜잭션 범위를 점검해 커넥션 점유 시간을 줄이고 풀 반환 지연을 완화함.
- 스레드·풀 지표를 모니터링하며 Pending thread와 timeout 비율이 안정화될 때까지 재조정함.
- P99 응답시간 30s → 1.2s.
- 부하 테스트에서
dial tcp: i/o timeout에러율 0%. - C10K 동시 요청 환경에서도 Pending thread 폭증 없이 안정적 처리량 유지.
flowchart LR
Client["Client"] --> ALB["ALB<br/>API Gateway"]
ALB -->|"구독 / SSE"| Noti["Notification WAS x2"]
ALB -->|"주문·결제 등"| Main["Main WAS"]
Main -->|"publish"| Redis["Redis Pub/Sub"]
Redis -->|"broadcast"| Noti
Main --> DB["MySQL"]
Noti --> Emitter["SseEmitter<br/>로컬 커넥션"]
- SSE 커넥션이 메모리를 독점해 알림 발생 시 주문·결제 등 핵심 기능 성능에 악영향이 생김.
- 모놀리스 구조에서 부가 기능(알림)이 메인 기능에 필요한 RAM까지 점유하는 주객전도 상황이 발생함.
- OS Send buffer 처리 속도와 단일 인스턴스 커넥션 수용 한계로 C50K 확장에 병목이 생김.
- TCP 소켓·FD는 특정 WAS 커널에만 존재 가능함을 확인하고, Emitter를 외부 저장소에 공유하는 구조를 배제함.
- 알림 WAS를 독립 분리하고 Spring Profile(
main/notification)로 Publisher·Subscriber 빈을 분기함. - Main WAS는 DB 반영 후 Redis
notification-topic에 publish하고, 알림 WAS는 Pub/Sub으로 수신해 로컬 Emitter에 전송함. - AWS ALB로 API Gateway를 구성해 SSE 구독은 알림 서버, 그 외 API는 Main WAS로 라우팅하고 Mesh·Cascading Failure를 완화함.
- 알림 서버 대당 약 15,000 커넥션, 합산 약 30,000 동시 SSE 구독 유지에 성공함.
- Main WAS 메모리를 주문·결제 등 비즈니스 로직에 확보하고, SSE 메모리 독점에 의한 상호 간섭을 제거함.
- OS Send buffer 부하를 인스턴스 단위로 분산해 커넥션 생성 지연을 완화하고, 단일 장애 지점 없는 고가용성 구조를 확보함.