Shortly는 Kafka와 Redis를 활용한 대규모 트래픽 처리에 최적화된 URL 단축 서비스입니다.
TPS: 13,622/sP95: 67.84ms캐시 히트율: 91.8% (L1 Caffeine + L2 Redis 멀티 레이어)데이터 유실 0건: Kafka At-Least-Once + 멱등성
- URL 단축
- 사용자는 원본 URL을 입력하면 단축 URL을 생성할 수 있다.
- 단축 URL은 중복이 발생하면 안 된다.
- 리다이렉트
- 사용자가 단축 URL에 접속하면 원본 리다이렉트된다.
- 통계 기능
- 단축 URL 별 클릭 기록 조회 가능
가용성: 서비스가 다운되면 모든 URL 리다이렉션이 실패하기 때문에 시스템은 높은 가용성을 가져야 한다.성능: URL 리다이렉션은 최소한의 Latency로 처리되어야 한다.무작위성: 단축된 URL은 추측이 예측 불가능하게 생성되어야 한다.
Backend:
- Java 21
- Spring Boot 3.5.6
- Kafka
- Redis
- Caffeine
- MySQL 8.0
Frontend:
- React 18
- TypeScript
- Vite
- React Router
- Axios
sequenceDiagram
participant Client
participant US as URL Service
participant DB as MySQL
participant Redis
participant RS as Redirect Service
Client ->> US: URL 단축 요청
US ->> US: Snowflake ID 생성
US ->> US: Base62 Encode
US ->> DB: 단축 URL 저장
US ->> Redis: URL 생성 이벤트 Pub
US ->> Client: 단축 URL 반환
Redis -->> RS: 이벤트 Sub
RS ->> RS: L1(Caffeine) + L2(Redis) 캐시 저장
sequenceDiagram
participant Client
participant RS as Redirect Service
participant L1 as Caffeine (L1)
participant L2 as Redis (L2)
participant US as URL Service
Client->>RS: 리다이렉션 요청
alt L1 Cache Hit
RS-->>Client: 302 Redirect
else L1 Miss
RS->>L2: L2 Cache(Redis) 조회
alt L2 Cache Hit
L2-->>RS: URL 반환
RS->>L1: L1 Cache Update
RS-->>Client: 302 Redirect
else L2 Miss
RS->>US: URL Service 요청(HTTP)
US-->>RS: URL 반환
RS->>L2: L2 Cache Update
RS->>L1: L1 Cache Update
RS-->>Client: 302 Redirect
end
end
문제: 클릭 이벤트는 과금 데이터로, 유실 시 매출 손실 발생
해결:
- At-Least-Once 전달 보장 (
acks=all+enable.idempotence) - Consumer DLQ로 영구 실패 메시지 보존
- 멱등성 처리로 중복 제거 (Snowflake ID + DB Unique Constraint)
성과: 130만 개 이벤트 발행 -> 유실 0건
문제: Read-Heavy 트래픽 (읽기:쓰기 = 100:1)에서 DB 부하 최소화 필요
해결:
- L1 (Caffeine): W-TinyLFU 정책으로 인기 데이터 보호 (LRU 대비 30% 히트율 향상)
- L2 (Redis): LFU 정책으로 인기 URL 장기 보존
- Pub/Sub: 캐시 워밍
- Cache Stampede 방지: LoadingCache + refreshAfterWrite
상세 보기:
개선 과정:
| 단계 | 병목 | 해결책 | TPS | P95 |
|---|---|---|---|---|
| Baseline | - | - | 7,365 | 133ms |
| 1차 개선 | Tomcat 연결 제한 | max-connections 1,500 증가 |
10,505 (+42%) | 141ms |
| 2차 개선 | Kafka 발행 블로킹 | Virtual Thread Executor 분리 | 13,622 (+85%) | 67ms (-49%) |