|
| 1 | +# 분산 서버 시스템 가이드 |
| 2 | + |
| 3 | +## 개요 |
| 4 | + |
| 5 | +ProjectVG API 서버가 이제 분산 환경을 지원합니다. Redis Pub/Sub를 통해 여러 서버 인스턴스 간 WebSocket 메시지를 라우팅하고, 사용자 세션을 추적할 수 있습니다. |
| 6 | + |
| 7 | +## 아키텍처 |
| 8 | + |
| 9 | +### 기존 단일 서버 구조 |
| 10 | +``` |
| 11 | +Unity Client ──WebSocket──→ API Server ──→ ChatService ──→ WebSocketManager |
| 12 | +``` |
| 13 | + |
| 14 | +### 새로운 분산 서버 구조 |
| 15 | +``` |
| 16 | +Unity Client ──WebSocket──→ API Server A |
| 17 | + ↓ |
| 18 | + MessageBroker |
| 19 | + ↓ |
| 20 | + Redis Pub/Sub |
| 21 | + ↓ |
| 22 | +API Server B ──WebSocket──→ Unity Client (실제 연결된 서버) |
| 23 | +``` |
| 24 | + |
| 25 | +## 주요 구성 요소 |
| 26 | + |
| 27 | +### 1. 서버 등록 시스템 |
| 28 | +- **목적**: 각 서버 인스턴스를 Redis에 등록하고 관리 |
| 29 | +- **구현**: `RedisServerRegistrationService`, `ServerLifecycleService` |
| 30 | +- **기능**: |
| 31 | + - 서버 시작 시 자동 등록 |
| 32 | + - 30초마다 헬스체크 전송 |
| 33 | + - 오프라인 서버 자동 정리 |
| 34 | + - 사용자-서버 매핑 관리 |
| 35 | + |
| 36 | +### 2. MessageBroker 추상화 |
| 37 | +- **목적**: 단일/분산 환경을 투명하게 지원 |
| 38 | +- **구현**: `IMessageBroker`, `LocalMessageBroker`, `DistributedMessageBroker` |
| 39 | +- **기능**: |
| 40 | + - 사용자별 메시지 전송 |
| 41 | + - 서버 간 메시지 라우팅 |
| 42 | + - 브로드캐스트 메시지 |
| 43 | + |
| 44 | +### 3. Redis Pub/Sub 시스템 |
| 45 | +- **채널 구조**: |
| 46 | + - `user:{userId}` - 특정 사용자 메시지 |
| 47 | + - `server:{serverId}` - 특정 서버 메시지 |
| 48 | + - `broadcast` - 전체 방송 메시지 |
| 49 | +- **메시지 라우팅**: 사용자가 연결된 서버로 자동 라우팅 |
| 50 | + |
| 51 | +### 4. WebSocket 세션 관리 |
| 52 | +- **분산 세션 추적**: Redis에서 사용자-서버 매핑 관리 |
| 53 | +- **연결/해제 처리**: 자동 채널 구독/해제 |
| 54 | +- **세션 TTL**: 30분 자동 만료 |
| 55 | + |
| 56 | +## 설정 방법 |
| 57 | + |
| 58 | +### 환경 변수 |
| 59 | + |
| 60 | +```bash |
| 61 | +# 분산 모드 활성화 |
| 62 | +DISTRIBUTED_MODE=true |
| 63 | + |
| 64 | +# 서버 고유 ID (자동 생성 가능) |
| 65 | +SERVER_ID=api-server-001 |
| 66 | + |
| 67 | +# Redis 연결 문자열 (필수) |
| 68 | +REDIS_CONNECTION_STRING=localhost:6380 |
| 69 | +``` |
| 70 | + |
| 71 | +### appsettings.json |
| 72 | + |
| 73 | +```json |
| 74 | +{ |
| 75 | + "DistributedSystem": { |
| 76 | + "Enabled": true, |
| 77 | + "ServerId": "api-server-001", |
| 78 | + "HeartbeatIntervalSeconds": 30, |
| 79 | + "CleanupIntervalMinutes": 5, |
| 80 | + "ServerTimeoutMinutes": 2 |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +## 사용법 |
| 86 | + |
| 87 | +### 단일 서버 모드 (기본) |
| 88 | + |
| 89 | +```bash |
| 90 | +# 환경 변수 설정 |
| 91 | +DISTRIBUTED_MODE=false |
| 92 | + |
| 93 | +# 또는 appsettings.json |
| 94 | +{ |
| 95 | + "DistributedSystem": { |
| 96 | + "Enabled": false |
| 97 | + } |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +- `LocalMessageBroker` 사용 |
| 102 | +- 기존 `WebSocketManager` 사용 |
| 103 | +- Redis 연결 불필요 |
| 104 | + |
| 105 | +### 분산 서버 모드 |
| 106 | + |
| 107 | +```bash |
| 108 | +# 환경 변수 설정 |
| 109 | +DISTRIBUTED_MODE=true |
| 110 | +SERVER_ID=api-server-001 |
| 111 | +REDIS_CONNECTION_STRING=localhost:6380 |
| 112 | + |
| 113 | +# 서버 시작 |
| 114 | +dotnet run --project ProjectVG.Api |
| 115 | +``` |
| 116 | + |
| 117 | +- `DistributedMessageBroker` 사용 |
| 118 | +- `DistributedWebSocketManager` 사용 |
| 119 | +- Redis 연결 필수 |
| 120 | + |
| 121 | +## 테스트 방법 |
| 122 | + |
| 123 | +### 1. 단일 서버 테스트 |
| 124 | + |
| 125 | +```powershell |
| 126 | +# 환경 변수 설정 |
| 127 | +$env:DISTRIBUTED_MODE="false" |
| 128 | +
|
| 129 | +# 서버 시작 |
| 130 | +dotnet run --project ProjectVG.Api --urls "http://localhost:7910" |
| 131 | +``` |
| 132 | + |
| 133 | +### 2. 다중 서버 테스트 |
| 134 | + |
| 135 | +```powershell |
| 136 | +# 서버 1 시작 |
| 137 | +$env:DISTRIBUTED_MODE="true" |
| 138 | +$env:SERVER_ID="api-server-001" |
| 139 | +$env:REDIS_CONNECTION_STRING="localhost:6380" |
| 140 | +dotnet run --project ProjectVG.Api --urls "http://localhost:7910" |
| 141 | +
|
| 142 | +# 서버 2 시작 (새 터미널) |
| 143 | +$env:DISTRIBUTED_MODE="true" |
| 144 | +$env:SERVER_ID="api-server-002" |
| 145 | +$env:REDIS_CONNECTION_STRING="localhost:6380" |
| 146 | +dotnet run --project ProjectVG.Api --urls "http://localhost:7911" |
| 147 | +
|
| 148 | +# 서버 3 시작 (새 터미널) |
| 149 | +$env:DISTRIBUTED_MODE="true" |
| 150 | +$env:SERVER_ID="api-server-003" |
| 151 | +$env:REDIS_CONNECTION_STRING="localhost:6380" |
| 152 | +dotnet run --project ProjectVG.Api --urls "http://localhost:7912" |
| 153 | +``` |
| 154 | + |
| 155 | +### 3. Redis 모니터링 |
| 156 | + |
| 157 | +```bash |
| 158 | +# Redis CLI 접속 |
| 159 | +redis-cli -p 6380 |
| 160 | + |
| 161 | +# 서버 등록 상태 확인 |
| 162 | +SMEMBERS servers:active |
| 163 | + |
| 164 | +# 특정 서버 정보 확인 |
| 165 | +GET servers:active:api-server-001 |
| 166 | + |
| 167 | +# 사용자 세션 확인 |
| 168 | +KEYS user:server:* |
| 169 | + |
| 170 | +# 메시지 채널 모니터링 |
| 171 | +MONITOR |
| 172 | +``` |
| 173 | + |
| 174 | +### 4. WebSocket 연결 테스트 |
| 175 | + |
| 176 | +```javascript |
| 177 | +// 각기 다른 서버에 연결 |
| 178 | +const ws1 = new WebSocket('ws://localhost:7910/ws?token=JWT_TOKEN'); |
| 179 | +const ws2 = new WebSocket('ws://localhost:7911/ws?token=JWT_TOKEN'); |
| 180 | +const ws3 = new WebSocket('ws://localhost:7912/ws?token=JWT_TOKEN'); |
| 181 | + |
| 182 | +// 메시지 전송 테스트 |
| 183 | +ws1.send(JSON.stringify({ |
| 184 | + type: 'chat', |
| 185 | + data: { message: 'Hello from server 1' } |
| 186 | +})); |
| 187 | +``` |
| 188 | + |
| 189 | +## 로그 확인 |
| 190 | + |
| 191 | +### 서버 등록 로그 |
| 192 | +``` |
| 193 | +서버 등록 서비스 초기화: ServerId=api-server-001, Timeout=00:02:00 |
| 194 | +서버 등록 완료: api-server-001 |
| 195 | +분산 시스템 모드 활성화 |
| 196 | +``` |
| 197 | + |
| 198 | +### 메시지 라우팅 로그 |
| 199 | +``` |
| 200 | +분산 사용자 메시지 전송: user123 -> api-server-002 |
| 201 | +분산 메시지 브로커 구독 초기화 완료: 서버 api-server-001 |
| 202 | +사용자 채널 구독 시작: user123 |
| 203 | +``` |
| 204 | + |
| 205 | +### 세션 관리 로그 |
| 206 | +``` |
| 207 | +새 분산 WebSocket 세션 생성: user123 |
| 208 | +분산 사용자 채널 구독 완료: user123 |
| 209 | +분산 WebSocket 세션 해제: user123 |
| 210 | +분산 사용자 채널 구독 해제 완료: user123 |
| 211 | +``` |
| 212 | + |
| 213 | +## 문제 해결 |
| 214 | + |
| 215 | +### Redis 연결 실패 |
| 216 | +``` |
| 217 | +Redis 연결 실패, In-Memory로 대체: Connection timeout |
| 218 | +``` |
| 219 | +- Redis 서버가 실행 중인지 확인 |
| 220 | +- 연결 문자열이 올바른지 확인 |
| 221 | +- 방화벽 설정 확인 |
| 222 | + |
| 223 | +### 서버 등록 실패 |
| 224 | +``` |
| 225 | +서버 등록 실패: api-server-001 |
| 226 | +``` |
| 227 | +- Redis 연결 상태 확인 |
| 228 | +- SERVER_ID 중복 여부 확인 |
| 229 | +- Redis 메모리 용량 확인 |
| 230 | + |
| 231 | +### 메시지 라우팅 실패 |
| 232 | +``` |
| 233 | +사용자가 연결된 서버를 찾을 수 없음: user123 |
| 234 | +``` |
| 235 | +- 사용자 세션이 만료되었을 가능성 |
| 236 | +- 대상 서버가 오프라인일 가능성 |
| 237 | +- Redis에서 사용자 매핑 확인: `GET user:server:user123` |
| 238 | + |
| 239 | +### WebSocket 연결 끊김 |
| 240 | +``` |
| 241 | +분산 환경에서 사용자를 찾을 수 없음: user123 |
| 242 | +``` |
| 243 | +- 사용자가 다른 서버로 이동했을 가능성 |
| 244 | +- 네트워크 연결 상태 확인 |
| 245 | +- 서버 간 시간 동기화 확인 |
| 246 | + |
| 247 | +## 성능 고려사항 |
| 248 | + |
| 249 | +### 메모리 사용량 |
| 250 | +- 서버당 약 1KB 메타데이터 |
| 251 | +- 사용자당 약 100B 세션 데이터 |
| 252 | +- Redis 키 TTL로 자동 정리 |
| 253 | + |
| 254 | +### 네트워크 대역폭 |
| 255 | +- 헬스체크: 30초마다 소량 데이터 |
| 256 | +- 메시지 라우팅: 실제 메시지 크기에 비례 |
| 257 | +- Redis Pub/Sub: 저지연 메시지 전달 |
| 258 | + |
| 259 | +### 확장성 |
| 260 | +- 수평적 확장: 서버 인스턴스 추가 가능 |
| 261 | +- Redis 단일 장애점: Redis Cluster 고려 |
| 262 | +- 로드밸런싱: API Gateway 또는 로드밸런서 사용 |
| 263 | + |
| 264 | +## 운영 가이드 |
| 265 | + |
| 266 | +### 모니터링 지표 |
| 267 | +- 활성 서버 수: `SCARD servers:active` |
| 268 | +- 총 사용자 세션 수: `KEYS user:server:* | wc -l` |
| 269 | +- 메시지 처리량: Redis MONITOR 활용 |
| 270 | +- 서버 헬스체크 간격: 로그 분석 |
| 271 | + |
| 272 | +### 유지보수 |
| 273 | +- 정기적 Redis 메모리 모니터링 |
| 274 | +- 오프라인 서버 자동 정리 확인 |
| 275 | +- 네트워크 지연 모니터링 |
| 276 | +- 로그 레벨 조정 (개발: DEBUG, 운영: INFO) |
| 277 | + |
| 278 | +### 백업 및 복구 |
| 279 | +- Redis 데이터는 일시적 (서버 재시작 시 초기화) |
| 280 | +- 서버 메타데이터만 저장하므로 별도 백업 불필요 |
| 281 | +- 장애 시 서버 재시작으로 자동 복구 |
| 282 | + |
| 283 | +이제 ProjectVG API 서버는 완전한 분산 환경을 지원합니다! |
0 commit comments