-
Notifications
You must be signed in to change notification settings - Fork 20
Open
0 / 10 of 1 issue completedLabels
Description
KCP 도입에 따른 아키텍처 변화
핵심 변경 사항은 **"전송 계층의 이원화(TCP Only → UDP 우선)"**와 선택적인 **"멀티플렉서 교체(Yamux → Smux)"**입니다.
1. AS-IS: 현재 아키텍처 (Yamux + TCP/WebSocket)
현재는 모든 트래픽이 TCP 기반(Raw TCP 또는 WebSocket) 위에서 돌아가며, 그 위에 Yamux가 올라가 있습니다.
- 문제점: TCP의 특성상 패킷 하나만 유실되어도 전체 스트림이 멈추는 HOL(Head-of-Line) 블로킹에 취약합니다.
- 구조: 단일 전송 경로에 의존적입니다.
graph TD
subgraph Client_Side ["Client Side"]
App[Application Logic]
RDSEC[RDSEC Encryption]
Yamux["<b>Yamux Session</b>"]
TCP[TCP / WebSocket]
App --> RDSEC
RDSEC --> Yamux
Yamux --> TCP
end
subgraph Network ["Network"]
Net["Internet<br/>(TCP Packet Loss = HOL Blocking)"]
end
subgraph Server_Side ["Server Side"]
S_TCP[TCP / WS Listener]
S_Yamux["<b>Yamux Server</b>"]
Handler[Relay Handler]
S_TCP --> S_Yamux
S_Yamux --> Handler
end
TCP <==> Net <==> S_TCP
style Yamux fill:#f9f,stroke:#333,stroke-width:2px
style S_Yamux fill:#f9f,stroke:#333,stroke-width:2px
2. TO-BE: 변경 후 아키텍처 (Smux + KCP/WS Unified)
Smux가 중심을 잡고, 하부 전송 계층을 **KCP(UDP)**와 WebSocket(TCP) 두 갈래로 나누어 관리합니다.
- 개선점:
- 성능: 네이티브 앱은 **KCP(UDP)**를 통해 HOL 블로킹 없이 고속 통신합니다.
- 호환성: 브라우저(WASM)는 WebSocket을 그대로 사용하되, 상위에서 Smux/Yamux로 통일되어 서버 로직 분기가 발생하지 않습니다.
- 효율: Smux는 Yamux보다 헤더 오버헤드가 작고 메모리 사용량이 적습니다. (선택적)
graph TD
subgraph Client_Side ["Client Side"]
App[Application Logic]
Noise[Noise Protocol Encryption]
Smux["<b>Smux Session</b><br/>(Unified Interface)"]
subgraph Transport_Selector ["Transport Selector"]
KCP["<b>KCP</b><br/>(Reliable UDP)"]
WS["WebSocket<br/>(TCP Fallback)"]
end
App --> Noise
Noise --> Smux
Smux --> KCP
Smux -.-> WS
end
subgraph Network ["Network"]
NetUDP["UDP Path<br/>(Fast, No HOL Blocking)"]
NetTCP["TCP Path<br/>(Browser/Firewall Compat)"]
end
subgraph Server_Side ["Server Side"]
KCP_L[UDP Listener :4017]
WS_L[TCP Listener :4017]
S_Smux["<b>Smux Server</b><br/>(Unified Handler)"]
Handler[Relay Handler]
KCP_L --> S_Smux
WS_L --> S_Smux
S_Smux --> Handler
end
KCP <==> NetUDP <==> KCP_L
WS <==> NetTCP <==> WS_L
style Smux fill:#4AB,stroke:#333,stroke-width:4px
style S_Smux fill:#4AB,stroke:#333,stroke-width:4px
style KCP fill:#F96,stroke:#333,stroke-width:2px
style KCP_L fill:#F96,stroke:#333,stroke-width:2px
구현 전략 요약
- 의존성 교체:
hashicorp/yamux제거 →xtaci/smux,xtaci/kcp-go/v5추가. - 서버 수정 (
main.go): UDP(KCP) 포트를 추가로 리스닝하고, TCP(WS)와 UDP 연결 모두io.ReadWriteCloser인터페이스로HandleConnection에 전달. - 핸들러 수정 (
relay.go):yamux.Server를smux.Server로 교체 (코드 구조는 동일). - 클라이언트 수정 (
sdk.go):kcp://스키마 지원 추가 및smux.Client적용.
세부 구현 전략 (Breakdown)
Phase 1: 의존성 교체 (Dependencies) [Optional]
기존 yamux를 제거하고 성능이 검증된 KCP 및 Smux 라이브러리를 도입합니다. (선택적, 만약 WASM WebClient를 Rust로 포팅한다면 yamux 유지가 더 feasible함)
- Remove:
github.com/hashicorp/yamux - Add:
github.com/xtaci/kcp-go/v5: KCP 프로토콜 Go 구현체 (FEC, Encryption 포함).github.com/xtaci/smux: 멀티플렉싱 라이브러리.
Phase 2: KCP 파라미터 튜닝 (Turbo Mode Configuration)
KCP의 성능을 극대화하기 위해 다음과 같이 설정합니다. 이는 게임이나 실시간 스트리밍 수준의 반응성을 제공합니다.
| 파라미터 | 설정값 | 설명 |
|---|---|---|
| NoDelay | 1 |
Nagle 알고리즘 비활성화 (데이터 즉시 전송) |
| Interval | 10ms |
내부 루프 주기 (기본 100ms → 10ms로 단축하여 반응성 10배 향상) |
| Resend | 2 |
ACK 2회 누락 시 즉시 빠른 재전송 (Fast Retransmit) |
| NC | 1 |
혼잡 제어(Flow Control은 유지하되 Congestion Control은 끔) |
| FEC | 10:3 |
데이터 10개당 패리티 3개 전송 (패킷 손실 30%까지 재전송 없이 복구) |
| Window | 1024 |
송수신 윈도우 크기 대폭 상향 (기본 32 → 1024) |
Phase 3: 서버 사이드 구현 (cmd/relay-server)
서버는 UDP와 TCP 포트를 동시에 열고, 들어오는 연결을 추상화하여 처리합니다.
// 개념적 코드 (Pseudo-code)
func runServer() {
// 1. WebSocket Listener (TCP)
go func() {
http.HandleFunc("/relay", func(w, r) {
conn := upgradeToWS(w, r)
handleUnifiedSession(conn) // WebSocket 연결 처리
})
http.ListenAndServe(":4017", nil)
}()
// 2. KCP Listener (UDP)
go func() {
listener, _ := kcp.ListenWithOptions(":4017", nil, 10, 3) // FEC 10+3
listener.SetDSCP(46) // QoS 설정
for {
conn, _ := listener.AcceptKCP()
// Turbo Mode 설정
conn.SetNoDelay(1, 10, 2, 1)
conn.SetWindowSize(1024, 1024)
conn.SetACKNoDelay(true)
go handleUnifiedSession(conn) // KCP 연결 처리
}
}()
}
// 3. 통합 핸들러 (Unified Handler)
func handleUnifiedSession(conn io.ReadWriteCloser) {
// 하부 프로토콜이 무엇이든 Smux 세션으로 래핑
smuxConfig := smux.DefaultConfig()
session, _ := smux.Server(conn, smuxConfig)
// 이후 로직은 기존과 동일하게 Stream Accept
serv.HandleSession(session)
}Phase 4: 클라이언트 구현 (sdk/ & cmd/webclient)
- Native Client (Go SDK):
- Race Dialing: KCP 연결을 우선 시도하고, 500ms 내 실패 시 WebSocket으로 즉시 전환합니다.
- KCP 연결 성공 시, 서버와 동일한 Turbo Mode 파라미터를 적용합니다.
- Web Client (WASM):
- 브라우저에서는 Raw UDP를 사용할 수 없으므로 WebSocket을 사용합니다.
- 중요: WebSocket 연결 후,
smux.Client(wsConn)을 호출하여 WASM 내부에서 Smux 프로토콜을 구동해야 합니다. 그래야 서버가 이를 KCP 클라이언트와 동일한 구조로 인식합니다.
Phase 5: 능동형 흐름 제어 (Active BPS Manager)
기존의 time.Sleep 기반 bps_manager를 제거하고, Smux/KCP의 Window Size를 조절하는 방식으로 변경합니다.
- Active Control: 사용자별 BPS 한도에 도달하면
session.SetStreamBuffer()등을 통해 윈도우 크기를 줄입니다. - Backpressure: 수신 윈도우가 줄어들면 송신 측(Sender)의 KCP 엔진이 알아서 전송 속도를 늦추므로, CPU 점유율 없이 자연스럽게 속도 제어가 가능합니다.
Reactions are currently unavailable