게임 서버 1대에 동시접속자 1만명 넣기
DESCRIPTION
프라우드넷에서 MMO 게임 서버 프로세스 1개에서 멀티 코어를 효율적으로 사용하기 위해 어떠한 기술을 구사했는지 소개합니다.TRANSCRIPT
서버 프로세스 1 개에 동시접속자 1 만명 넣기
배현직Nettention
내 소개
突击 1941
• 1995 년부터 게임회사에서 근무• 토끼와 거북이 3D, Menticide 격투
게임• 오즈 월드 , 블리츠 1941
• Game Programming Gems 5,7
• ProudNet• KGC 2010, 2011, 독일 Quo Vadis
2012, 중국 CGDC 2012, 일본 CEDEC 에서 강연
• 로비형 MO 게임 – 쉽게 해결– 서버 머신 증설만 하면 됨
RoomServer
RoomServer
RoomServer
RoomServer
RoomServer
RoomServer
RoomServer
RoomServer
• 월드 개수가 고정된 MMO 게임– 쉽게 해결– CPU 개수만큼 월드 서버들을 띄우면 됨– 단 , 1 개 CPU 가 1 개 월드를 처리할 수 있으면
ZoneServer
ZoneServer
ZoneServer
ZoneServer
ZoneServer
ZoneServer
ZoneServer
ZoneServer
• 월드 서버 1 개가 CPU 한 개 사용량을 넘어가면 ?
• 서버 프로세스 로직의 병렬화가 필요 !
Zone Server
Zone Server
10,000 소켓 쉬움
그리고 일대일 통신 그럭저럭
그리고 일대다 통신 빡셈
Player_Move
Player_ShowM
ove
Player_ShowM
ove
Player_ShowM
ove
MMO 게임 서버는 이러함 !
용어 정의
Lock(X)
class MyType{ CritSec m_critSec; int a;}
MyType X;
void Something(){ CritSecLock lock(X.m_critSec); ...; }
컨텍스트 스위치
Thread 2Thread 1
컨텍스트 스위치
컨텍스트 스위치
Thread 1{ lock(A); A = A+1; lock(A);}
Thread 2{ lock(A); A = A * 2; unlock(A);}
Object A
Contention
CPU 1 CPU 2
CPU cache sharing!
granulation
A
B
C
D
A
B
C
D
리소스 분할
우리가 겪은 문제
목표
CPU %
동시접속자 + 트래픽
1 개 서버 프로세스가 모든 CPU 를 적절히
활용해야
𝑟=1
(1−𝑃 )+ 𝑃𝑆
처리의 40 %가 병렬화 (P = 0.4)2 배 성능으로 병렬화 (S = 2)
그러나 전체 성능은 겨우 25% 향상(r = 1.25)
Amdahl 의 법칙
Amdahl 의 법칙저주
빈공간이 Amdahl 의 저주
과거에 우리의 상황• 우리가 이러한 것들을 무시했다면
– 하위 호환성– 안정성
• 고객사들이 곤란했을 상황 !
마비노기 영웅전 마계촌 online S4 리그 러스티 하츠
그리고 다른 많은 고객사들
결국 해결 ! 어떻게 ?
APPLE MMLXXX][
]10 프로파일&분석]20 테스트 코딩과 엎어버리기의 반복 ]30 설계 문서 작성]40 구현]50 GOTO 10
]RUN
APPLE MMLXXX][
]10 프로파일&분석]20 테스트 코딩과 엎어버리기의 반복 ]30 설계 문서 작성]40 구현]50 GOTO 10
]RUN
가장 심한 병목• 비동기 socket I/O 함수 호출–WSASend, WSASendTo–WSARecv, WSARecvFrom
send(), recv() ( 블러킹이 일어남 )WSASend, WSARecv ( 블러킹 안 일어남 )
무지무지 긴 idle 시간짧은 CPU 시간
Q. 잠깐 , IOCP 가 최고 빠른 것 아니었어 ?
A. 최고 빠른 것 맞음 .다만 , 사용자 루틴에 비해 느리다는 것이 함정 .
그럼에도 불구하고이것이 병목 !
APPLE MMLXXX][
]10 프로파일&분석]20 테스트 코딩과 엎어버리기의 반복 ]30 설계 문서 작성]40 구현]50 GOTO 10
]RUN
A 를 스퀴즈
B 를 분할하기
Main
Remote host
P2P pair
P2P group
Etc.
이것을 스퀴즈해야 !
Main
Remote host
P2P pair
P2P group
Etc.
심장 심장 + 폐
뇌사 환자
Remote Ob-ject
Joined P2P group stateP2P pair relationship
Traffic status…
Socket handleSend buffer
Receive bufferOverlapped I/O status
…
Main
Remote Ob-ject
Joined P2P group stateP2P pair relationship
Traffic status…
Socket handleSend buffer
Receive bufferOverlapped I/O status
…
Main
잦은 잠금은 빡셈 !
안 잠궈진 객체가 살아있음을 어떻게 보증해 ?
Contention 과 잠금 비용과의 관계
Contention-less lockContention-
less lockContention-less lock
Contention-inten-sive lock<
Contention-less lock
Contention-less lockContention-
less lockContention-less lock
Contention-less lock
Contention-less lockContention-
less lockContention-less lock
Contention-less lock
Contention-less lock
잠금 방법에 따른 비용
non-blocking locknon-blocking
locknon-blocking lock
Blocking lock<non-blocking
lock
non-blocking locknon-blocking
locknon-blocking lock
non-blocking lock
non-blocking locknon-blocking
locknon-blocking lock
non-blocking lock
non-blocking lock
원자적 실행 명령어(Atomic operations)
• InterlockedIncrement• InterlockedExchange• InterlockedCompareExchange• InterlockedBlahBlah• InterlockedBlahBlah• InterlockedBlahBlah• InterlockedBlahBlah
Thread 1{ Get B from A; lock(B); do(B);}
Thread 2{ Get B from A; delete B;}
Object A
Object BObject B
CRASH!
Thread 1{ Get B from A; B.use_count+1; lock(B); do(B); unlock(B); B.use_count-1;}
Thread 2{ Get B from A; if(B.use_count=0) delete B;}
Object A
Object B Keep!
Main
Remote host
P2P pair
P2P group Etc.
비파괴 보장
Before squeeze
After squeeze
Lock(main)
Lock(remote#1)
Lock(main)
Lock(remote#2)
Lock(main)
Lock(remote#1)
Lock(main)
Lock(remote#2)
Lock(main)
Lock(remote#3)
Lock(main)
Lock(remote#4)
Amdahl’s Curse
Amdahl’s Curse
Am-dahl’s Curse
Am-dahl’s Curse
Am-dahl’s Curse
Lock(main)
APPLE MMLXXX][
]10 프로파일&분석]20 테스트 코딩과 엎어버리기의 반복 ]30 설계 문서 작성]40 구현]50 GOTO 10
]RUN
대부분의 경우• Lock(main)• r.use_count+1• Unlock(main)• Lock(r)• work(r)• Unlock(r)• r.use_count-1
Main
Remote host
P2P pair
P2P group
비파괴 보장
좀비 오브젝트 파괴• Lock(main)• if r.use_count=0
&& r 의 io 가 더 이상 없음이 체크되어 있으면
• r 제거• 그렇지 않으면 , 다음
기회에 다시 시도
Main
Remote host
P2P pair
P2P group
USE COUNT = 0?
컨텍스트 스위치를 줄이지 않은 루프
Main
Remote 1 Remote 2 Remote 3
CPU time
Idle time!!
CPU time
CPU time
CONTEXT SWITCH!
컨텍스트 스위치를 줄인 루프
Main
Remote 1 Remote 2 Remote 3
CPU time
CPU time
CPU time
NO CONTEXT SWITCH!(if lucky)
1 개 이상의 상대에의 송신 걸기
• lock(main)• 각 r 에 대해 , r.use_count+1• unlock(main)• 각 r 에 대해 , non-blocking
lock 을 수행• Lock(r) 성공시 , r 의 비동기
송신을 건 후• Lock(r) 성공시 unlock(r) &
r.use_count-1• 실패한 r 들에 대해서만 다시 위
과정을 반복 . 단 , 첫번째 항목에 대해서는 blocking lock 을 수행해야 .
Main
Remote host
P2P pair
P2P group
비파괴 보장
송신 완료시
• GetQueuedComple-tionStatus returns r
• r.use_count+1• lock(r)• r 의 비동기 송신을 걸고• unlock(r)• r.use_count-1
Main
Remote host
P2P pair
P2P group
비파괴 보장
수신 완료시
• GetQueuedCompletionStatus returns r
• r.use_count+1• lock(r)• Extract messages• Unlock(r)• Lock(main)• Process messages• Unlock(main)• lock(r)• r 의 비동기 수신을 걸고• Unlock(r)• r.use_count-1
Main
Remote host
P2P pair
P2P group
비파괴 보장
APPLE MMLXXX][
]10 프로파일&분석]20 테스트 코딩과 엎어버리기의 반복 ]30 설계 문서 작성]40 구현]50 GOTO 10
]RUN
테스트 케이스• 운 좋게도 우리는 이미 이런게 있었다– 자체 개발한 테스트 시스템– 여러가지 스트레스 테스트 앱• MMO case• P2P casual game case • P2P super peer case
• 자체제작된 자동화된 유닛 테스트• 여러 인터넷 공유기• 여러 가상 머신들
Belkin G WirelessTPLINK WR304G+TPLINK V108COSY BR674WLIPTIME G104MIPTIME Q304BUFFALO AIRSTATION
UNICORN WB1000
NETGEAR WGR614V9
DLINK DIR300LINKSYS WRV200MERCURY MR804TPLINK TLR410+TPLINK TLR402Samsung SWP1000
ZIO V10IPTIME N5004NEXT 915VSMC WBR14-G2ZYXEL NBG417NLINKSYS WRT54G2
AZTECH WL830RT4
ASUS RX3041DLINK DIR600PROLINK WNR1004LG-1000
리그레션 테스트용For regress test
스트레스 테스트
Windows 2008 Server x64Xeon CPU
Before
after
APPLE MMLXXX][
]10 프로파일&분석]20 테스트 코딩과 엎어버리기의 반복 ]30 설계 문서 작성]40 구현]50 GOTO 10
]RUN
마무리
평소에 공부하자• 컨텍스트 스위치 주의• 컨텐션 주의• 커널 API 호출 주의
공부한 것을 실무에서 이렇게• 최적화는 가급적 마지막에
코드 프로필러를 꼭 써야• 리소스 분할 기법• 스퀴징• 컨텍스트 스위치가 적은 루프
보너스 : 프라우드넷 소개
프라우드넷 3 가지 구성 요소
P2P
C/S
DB cache
Server-to-serverLANClient-to-server
WAN
프라우드넷 기본 사용 예DB cache
Server-to-serverLANClient-to-server
WAN
Connect()
JoinP2PGroup()
MyMessage(sendTo, a,b,c);
LoadData()UniUpdateData()
JoinP2PGroup()
감사합니다 !