[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버Heungsub Lee
NDC14에서 발표한 "[야생의 땅: 듀랑고] 서버 아키텍처" 세션의 슬라이드입니다.
슬라이드에 설명이 많지 않은데, 디스이즈게임에서 발표 내용을 잘 정리해주었습니다. 기사도 함께 보시면 좋을 것 같습니다.
http://www.thisisgame.com/webzine/news/nboard/4/?n=54955
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
Windows IOCP vs Linux EPOLL Performance ComparisonSeungmo Koo
1. The document compares the performance of IOCP and EPOLL for network I/O handling on Windows and Linux servers.
2. Testing showed that throughput was similar between IOCP and EPOLL, but IOCP had lower overall CPU usage without RSS/multi-queue enabled.
3. With RSS/multi-queue enabled on the NIC, CPU usage was nearly identical between IOCP and EPOLL.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
Windows IOCP vs Linux EPOLL Performance ComparisonSeungmo Koo
1. The document compares the performance of IOCP and EPOLL for network I/O handling on Windows and Linux servers.
2. Testing showed that throughput was similar between IOCP and EPOLL, but IOCP had lower overall CPU usage without RSS/multi-queue enabled.
3. With RSS/multi-queue enabled on the NIC, CPU usage was nearly identical between IOCP and EPOLL.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
[25]안드로이드에서 코루틴은 어떻게 적용할 수 있을까?: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?NAVER Engineering
안드로이드에서 코루틴은 어떻게 적용할 수 있을까?
: 코루틴 적용 및 ReactiveX(RxJava/RxKotlin)와 비교한다면?
권태환
요기요 / 안드로이드 개발
6년차 안드로이드 개발자 권태환입니다. 저는 꿈 많은 개발자가되자라는 블로그를 운영하고있고 GDG Seoul의 운영진을 하고 있습니다. 현재는 RGP Korea에서 요기요앱을 개발하고있으며, 안드로이드 개발 패턴과 코틀린에 흥미를 가지고 실무에도 적용하고 있습니다.
2. 동작 원리
IOCP의 목적
IO 작업에서 동시 수행되는 스레드 개수의 상한을 설정하자!
그래서 CPU 자원을 최대한 효율적으로 사용하자!
3. 동작 원리
IOCP 생성
당연히 IOCP를 쓰려면 먼저 IOCP를 만들어야한다.
원형
CreateIoCompletionPort(
HANDLE fileHandle, // IOCP와 연결할 핸들. 첫 생성시는
// INVALID_HANDLE_VALUE 넘김
HANDLE ExistingCompletionPort, //IOCP 핸들. 역시 첫 생성시는 NULL.
ULONG_PTR CompletionKey, //IO 완료시 넘어갈 값. 사용자가 넘기고 싶은 값 넘김.
DWORD NumberOfConcurrentThreads //한 번에 동작할 수 있는 최대 스레드 개수.
//0 넘기면 프로세서 숫자로 자동 지정됨.
4. 동작 원리
IO 장치와 IOCP 연결 - 마찬가지로 CreateIoCompletionPort를 쓴다.
HANDLE
HANDLE hPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
HANDLE port = CreateIoCompletionPort(socket,hPort,(ULONG_PTR)id,0);
//error
if(port != hPort) return false;
장치 리스트
새로운 레코드
추가
hDevice CompletionKey
레코드 추가 시점
CreateIoCompletionPort 함수로 장치 연결시
레코드 삭제 시점
해당 장치의 핸들이 닫혔을 때
CreateIoCompletionPort를 호출하
면 내부적으로 IOCP와 연결된 여러 장
치들을 관리하기 위한 장치 리스트에 새
로운 레코드를 추가한다.
5. 동작 원리
장치와 연결이 끝나면 IO 작업이 완료된 후 완료된 IO에 대한 처리를 수행
할 스레드 풀을 구성한다. 일반적으로 스레드 풀의 크기는 프로세서 개수의
2배 정도를 할당한다(이유는 이후 구조 설명 후 다시 살펴보자).
for(int i = 0; i < ioThreadCount; i++)
{
DWORD ThreadId;
HANDLE hThread = (HANDLE)_beginthreadex(NULL,0, workerThread,
(LPVOID)i,0,(unsinged int)&dwThreadId);
}
6. 동작 원리
IO Completion Queue
dwNumberOfBytes
Transferred
dwCompletionKey lpOverlapped dwError
레코드 추가 시점
I/O 요청이 완료되었을 때
PostQueuedCompletionStatus 함수 호출시
레코드 삭제 시점
IO completion Port가 Wait Thread Queue의 항목을 가져올 때
즉 이 큐는 IOCP와 연결한 device의 IO 작업이 끝났음을 알려주는 큐이다.
방금 전 생성한 스레드 풀에서 적절한 스레드들이 IO Completion Queue
에서 작업거리를 꺼내 IO 완료에 따른 처리를 수행하게 된다.
7. 동작 원리
Waiting Thread Queue(*LIFO)
dwThreadId
레코드 추가 시점
GetQueuedCompletionStatus 함수가 호출되었을 때
레코드 삭제 시점
IO Completion Queue가 비어있지 않고 수행 중인 스레드의 개수가 동시 수행 가능한 스레드 개수를 초과하지 않은 경우
이 큐는 IO 작업의 완료를 처리하기 위해 대기하고 있는 스레드들의
큐이다. GetQueuedCompletionStatus 함수를 호출했을 때 당장 IO
Completion Queue에 항목이 없거나 동시 수행 가능한 스레드 개수를
초과한 경우 대기 큐에 들어가 대기하고 있다가 적당한 상황이 오면
깨어나서 IO 완료에 대한 처리를 수행하게 된다.
8. 동작 원리
Release Thread List
dwThreadId
레코드 추가 시점
IO Completion Port가 Waiting Thread Queue에 있는 스레드를 깨우는 경우
일시정지되었던 스레드가 다시 깨어났을 경우
레코드 삭제 시점
스레드가 다시 GetQueuedCompletionStatus 함수를 호출했을 때
스레드가 정지되는 함수를 호출했을 때
실제 IO 완료에 따른 처리를 수행하기 위해 깨어나서 돌고 있는 Thread
들의 리스트이다. IOCP에서 지정한 개수만큼의 스레드만 이 리스트에 속할
수 있다(그 만큼만 깨어있을 수 있다).
9. 동작 원리
Paused Thread List
dwThreadId
레코드 추가 시점
수행 중이던 스레드가 스레드의 정지시키는 함수를 호출했을 때
레코드 삭제 시점
일시정지되었던 스레드가 다시 깨어날 경우
작업을 처리하던 스레드가 특정 함수의 호출(스레드를 블락시키는 함수 등)
했을 때 이 스레드를 Paused Thread List로 보낸다.
12. 예시
장치 리스트
socket
Waiting Thread Queue에는 처음 생성한 스레드 풀에서
GetQueuedCompletionStatus 함수 호출을 통해 들어간 4개
의 스레드가 대기중인 상황이다.
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2 Thread 3 Thread 4
13. 예시
장치 리스트
socket
아직 완료된 IO 작업이 없으니 Release Thread List와 Pause
Thread List도 비어있는 상태.
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2 Thread 3 Thread 4
Release Thread List
Pause Thread List
14. 예시
장치 리스트
socket
이 때 2개의 IO 작업이 완료되었다고 하자.
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2 Thread 3 Thread 4
Release Thread List
Pause Thread List
Completion 1 Completion 2
15. 예시
장치 리스트
socket
IO Completion을 처리하기 위해 Waiting Thread Queue
에 가장 최근에 들어온 스레드 2개를 깨우게 된다(Waiting
Thread Queue는 성능을 위해 LIFO 순으로 Thread를 꺼낸
다).
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2 Thread 3 Thread 4
Release Thread List
Pause Thread List
Thread 4 Thread 3
16. 예시
장치 리스트
socket
Release Thread List에 2개가 돌고 있을 때, 또 다시 새로운 IO
작업이 완료되었다고 하자. 이 때 이미 Relase Thread List에 2
개의 Thread가 돌고 있기 때문에 새로운 스레드는 깨우지 않는
다.
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2
Release Thread List
Pause Thread List
Thread 4 Thread 3
Completion 3 Completion4
17. 예시
장치 리스트
socket
이 때 Thread4에서 어떤 함수가 호출되어 이 Thread가 대기 상
태에 빠지게 된다면 어떻게 될까?
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2
Release Thread List
Pause Thread List
Thread 4 Thread 3
Completion 3 Completion4
Thread 4
18. 예시
장치 리스트
socket
IOCP는 항상 스레드 개수를 사용할 수 있는 한 많이 사용하려
고 한다. 그래서 이 경우 1개만 돌고 있으므로 Waiting Thread
Queue에서 새로운 스레드를 꺼낸다(이 때문에 프로세서 개수
보다 많은 스레드가 필요한 것이다!).
IO Completion Queue
Waiting Thread Queue
Thread 1 Thread 2
Release Thread List
Pause Thread List
Thread 3
Completion 4
Thread 4
Thread 2
19. 예시
장치 리스트
socket
그런데 이 상태에서 Pause Thread List의 스레드가 대기 상태
의 작업이 끝난다면? 이 걸 그냥 깨우면 Release Thread List의
최대 Thread 개수를 초과하게 되어버린다.
IO Completion Queue
Waiting Thread Queue
Thread 1
Release Thread List
Pause Thread List
Thread 3
Completion 4
Thread 4
Thread 2
이제 대기 상태 끝!
20. 예시
장치 리스트
socket
그래서 이 경우 IOCP는 Release Thread List의 스레드가 다시
대기 상태에 들어가기 전까진 스레드를 깨우지 않는다.
IO Completion Queue
Waiting Thread Queue
Thread 1
Release Thread List
Pause Thread List
Thread 3
Completion 4
Thread 4
Thread 2
불가능!
21. 코드(GQCS/PQCS)
각각의 IO Worker Thread에서는 아래와 같은 코드를 수행해서 IO 완료에 따른 처리를 하게 된다.
while(true)
{
int ret = GetQueuedCompletionStatus(port,&numBytes,&completionKey,&overlapped,TIME_OUT);
if(ret == 0 && GetLastError() == WAIT_TIMEOUT)
continue;
//그 외 경우 에러 처리는 생략. overlapped가 nullptr이라든가 numBytes가 이상하다든가 하는 경우들이 있음. 보통 연결 종료.
//completionKey가 종료된 IO작업 종류를 담고 있다고 가정
if(completionKey == IO_RECV)
{
//recv 관련 작업 수행
}
else if(completionKey == IO_SEND)
{
//send 관련 작업 수행
}
}
PQCS는 IO Completion Queue에 새 요소를 집어넣을 수 있게 해준다. 다른 스레드에 뭔가 완료
통지를 보내고 싶을 때 유용. 인자는 GQCS와 거의 동일(GQCS 호출시 필요한 데이터 거의 그대로
넘김)
//example. 대기중인 다른 스레드 종료시키기(THREAD_RELEASE가 스레드 종료 이벤트라고 가정).
PostQueuedCompletionStatus(port, 100, THREAD_RELEASE , overlapped);
22. 코드(SEND/RECV)
send, recv를 호출하는 부분에서는 아래와 같은 방식으로 코드를 작성하게 된다.
WSARecv(clntSock, //클라이언트 소켓.
&ioContext->wsaBuf, //읽을 데이터 버퍼의 포인터.
1, //데이터 입력 버퍼의 개수.
nullptr, //recv 결과 읽은 바이트 수. IOCP에서는 비동기 방식으로 사용하지 않으므로 nullptr 넘겨도 무방.
&flags, //recv에 사용될 플래그.
&ioContext->overlapped), //OVERLAPPED 구조체의 포인터
nullptr); //completion routine 함수의 포인터. IOCP에서는 사용하지 않으므로 nullptr 넘겨도 무방.
WSARecv(clntSock,
&ioContext->wsaBuf,
1,
nullptr,
0,
&ioContext->overlapped),
nullptr);
recv나 send나 거의 똑같다. io 작업을 위한 버퍼와 OVERLAPPED 구조체의 포인터를 넘겨준다.