차세대 실시간 게임서버 엔진 Century 설계 설명

피터 틸의 <ZERO to ONE> 은 이런 물음으로 시작된다:

사람을 채용하려고 면접을 볼 때 내가 자주 하는 질문이 하나 있다.

“정말 중요한 진실인데 남들이 당신한테 동의해주지 않는 것은 무엇입니까?”

그리고 책 중간에도 이런 문장들이 있다:

“대부분의 사람들은 더 이상 찾아낼 숨겨진 비밀이 없는 것처럼 행동한다.” (P126)

“사람들이 숨겨진 비밀을 무서워하는 것은 틀릴까 봐 무서워하기 때문이다. 숨겨진 비밀이라면 당연히 주류 세력의 점검을 받은 적이 없다. 인생에서 실수하지 않는 것이 목표인 사람은 숨겨진 비밀을 찾아다니면 안 된다. 혼자서만 옳은 것도 쉬운 일이 아닌데, 혼자이면서 ‘틀리는 것’ 은 견딜 수 없을 테니 말이다.” (P130)

“세번째 추세는 ‘무사 안일주의’ 다. 사회의 엘리트들은 새로운 사고를 탐구할 수 있는 자유와 능력을 가장 많이 지녔다. 하지만 그들이야말로 숨겨진 비밀을 가장 믿지 않는 사람들인 것 같다. 이미 다 해놓은 것들을 토대로 편안하게 지대(地代)나 받고 있으면 되는데, 무엇하러 새로운 숨겨진 비밀을 찾아 해매겠는가?” (P130)

“숨겨진 비밀을 찾겠다는 포부를 가진 사람들은 먼저 이렇게 자문하게 될 것이다. ‘뭔가 새로운 것을 발견하는 게 가능하다면 똑똑하고 창의적인 글로벌 인재들 중 누군가가 벌써 발견하지 않았을까?’ ” (P131)

“숨겨진 비밀은 찾아다니지 않으면 발견할 수가 없다. 이 점을 잘 보여준 사람이 페르마의 마지막 정리를 증명한 앤드루 와일스였다.” (P135)

“진짜 진실은 아직 찾아내지 못한 숨겨진 비밀들이 많이 있다는 점이다. 하지만 그 비밀들은 오직 그칠 줄 모르고 찾아 헤매는 사람들에게만 그 모습을 드러낼 것이다.” (P136)

“숨겨진 비밀을 믿고 그것을 찾아다니는 것만으로도 우리는 보편화된 관습을 넘어 뻔히 보이는 곳에 숨어 있는 기회들을 볼 수 있다.” (P137)

“숨겨진 비밀을 발견한 사람은 선택의 기로에 놓인다. 누군가에게 얘기를 할 것인가? 아니면 혼자서 간직할 것인가? 이는 숨겨진 비밀의 종류에 달려 있다. 파우스트가 바그너에게 들려준 것처럼 위험한 진실들도 있으니까 말이다.” (P141)


이런 방법론과 정신에 대한 예의로, 일단 떠나보기로 하겠다. 그래야 헛배우지 않은 거라고 생각된다.

지금 이 글 같은 글들은 욕멀을게 뻔하다. 심지어 “욕을 벌려고 글을 썼다” 고 욕먹을 가능성도 크다.

그럼에도 불구하고, 숨겨진 진실들을 폭로한다.

실시간 게임서버 개발의 숨겨진 진실들

그릇된 통념들:

통념 1: 게임 서버에게는 CPU 성능이 제일 중요하다

사실은 IO boundary (물리나 공간관계 연산이 많지 않은 한)

Redis 도 IO boundary. (이 문장은 필자가 내린 결론이 아니라 redis 개발자가 내린 결론.)

여기에서는 논거나 논증을 생략하겠다. 업계 구루가 쓴 책에서도 내린 결론이다. 다만 충분히 널리 알려지지 않았다고 생각해서 열거한거다.

통념 2: 멀티스레드는 싱글스레드보다 빠르다

통념 1 의 결과를 참조하면 알 수 있는데, 게임서버에 있어서 무거운 연산 타스크가 없는 한, 병목이 아닌 상태변경자(Business Logic Processor, BLP)를 병렬화하는 것은 성능향상을 가져올 수 없을뿐더러 동기화의 부담(성능부담 & 복잡성 부담)을 가져온다.

통념 2.1: 상태 변경자는 느리다.

진실: 상태 변경자는 매우 빠르다. 전체 게임서버에서 상태변경자만큼 빠른 부분도 없다. (LMAX를 봐라) (이 부분은 순수(pure)하다.)

통념 2.2: lock-free 를 사용하면 동기화문제에서 벗어날 수 있다.

진실: lock-free 도 동기화이다(CPU instruction 입도의 동기화일뿐). atomic 연산도 비교적 느리다.

추가로 lock-free 는 쉽지 않다. 특히 게임 서버에서 흔히 사용하는 객체들 예를 들어 player 등은, lock-free 자료형으로 표현하기 쉽지 않다. 다른 흑마법들이 필요하다.

redis vs memcached

LMAX 6M TPS

(LMAX architecture 소개글, not only how, but also why)

주의: 비즈니스 로직 프로세서, 즉 상태변경자에서 스크립트를 무절제하게 사용하기 시작하면 상태변경자가 병목이 될 수도 있고, 병렬화를 통한 이득을 볼 수도 있다.

통념 3: 일부 멀티스레드 프로그램은 버그가 가득하다.

진실: 스레드를 사용하는 대부분의 프로그램들은 버그로 가득 차 있다.

— Havoc Pennington @ Dreamforce 2011

RWlock 을 spinlock 으로 바꿨다고 자만하고 있을지도 모르지만, spinlock 도 블락킹이다. (일례로)

통념 4: 게임서버는 빠를수록 좋다

진실: 일정한 수준 (redis 정도) 이상 올라가면 추가의 성능향상은 의미가 없다

고성능 테크닉들 ( STL 사용 거부, try..catch.. 거부, 스마트 포인터 거부 등등.. 이 리스트는 아주 길 수 있지만 여기서는 생략한다) 을 사용하여 사실 redis 보다 한개 수량급(즉 10배) 빨라질 수 있다. 하지만 대부분의 콘텐츠개발자는 이런 테크닉들에 대한 요해가 부족하거나 이런것까지 신경쓸 시간적 여유가 안된다. 동적 메모리 할당을 무분별 도입하는 순간, 예외처리를 도입하는 순간, 코어 레이어에서 사용한 모든 고성능 테크닉들의 가치는 신속하게 소멸될 것이며 전체 시스템 스루풋은 redis 수량급으로 떨어진다(사실 redis와 비슷한 성능을 내주면 이미 굉장히 잘한 것이다).

결국 (절대적이진 않지만) 당신이 게임서버 엔진을 어떻게 짜든 당신의 실시간 게임 서버는 redis 보다 현저하게 (하지만 수량급은 같을 수 있고 또 같아야 한다) 느릴 수 밖에 없다.

통념 5: 벤치마크는 유용하다

진실: 벤치마크 결과비교는 늘 생산성의 적인데, 후자가 훨씬 중요하다.

LIES, DAMN LIES, AND BENCHMARKS

라는 업계 클리셰도 있다.

VM 개발자들이 늘 하는 말:

마이크로 벤치마크 결과를 믿지 마라.

프로그래밍은 결국은 복잡성에 대한 통제이다. 그렇지 않으면 모든 프로그램 개발에서 어샘블리 언어를 사용하지 않는 이유를 설명할 수가 없다. Node.js 작자가 libevent (실은 당시는 libev였고 현재는 libuv라지만)를 사용하여 node.js 를 만든 목표는 libevent의 성능을 향상시키기 위함이 아니라 복잡성을 다스리기 위함이였는데, 후자가 훨씬 “0에서 1을 창조”한 셈이다.

사내 정치, 커뮤니티 입소문으로는 생산성의 가치가 굉장히 저평가되고, 반면에 일견 증거가 든든해보이는 벤치마크 결과들이 과대평가 될 수 밖에 없는 구조다. 생산성은 양화하기 어렵기 때문이다.

통념 6: 서버 노드당 동접수는 중요하다

진실: (경제 계산상) 물리기계 대당 동접수가 중요하지 프로세스당 동접수는 중요하지 않다.

Seamless MMOG 를 제외한 대부분의 게임 서버 시스템은 분산 저항성 (Partition Tolerance) 이 그렇게 약하지 않다. Seamless MMOG 일지언정, 대부분의 경우는 멀티스레딩 ( or 멀티스레딩 + 멀티 프로세싱) 은 멀티프로세싱 only 보다 분산으로 인한 (트래픽) 부하 패널티가 그렇게 크지 않다.

일부 논점들에는 근거들을 대지 않았는데, 그런 부분들은 모두 업계에 이미 잘 알려져 있어서 논증할 필요가 없다고 판단한 부분들이다. 물론 매 한마디 말에도 반대하는 사람이 많을지도 모른다. 그런 말들에 일일이 반박할 수는 없을 것 같다.


차세대란:

64 core 기기 에서 우아하게 돌아가야 한다. 4 core 에서도 우아하게 돌아야 한다.

싱글스레드로 성능을 뽑기 보다는 비즈니스 로직 프로세서를 느린 언어로 여유롭게 짤 수 있을 만큼 병렬화가 고통스럽지 않게 해줄 수 있어야 한다.

여기서 느린 언어란 python, ruby 같은 정말로 느린 언어보다는, 현대 C/C++ 컴파일러의 최적화 옵션들을 최대로 켰을 때의 컴파일 결과물과 비교했을 때 느리다는 뜻. 필자의 이상대로라면 go 1.5 버전 정도가 적절.

차세대에서는,

현시대에서는 견딜 수 있는 멀티프로세싱의 과다 분산 패널티가, 차세대에서는 받아들일 수 없을만큼 병목이 될 수 있다. 멀티코어 파워를 쉽게 이용하는 능력이 굉장히 중요해질 수 있다.

이상적인 상황:

In-thread 마이크로 서비스 모듈화 대신, 노드별 (프로세스별 / 기기별) 서비스 분리. (SOA, Service Oriented Architecture)

Core 는 C++로 고성능 내주고, 주변(bgsave, db read 등)은 go 언로를 사용함으로 생산성 높이기.

논증 후 결과:

머리가 아주 노력하여 논리로 직관을 저항해야 심장의, 우아한 boost.asio 로 코어를 개발하겠다는 충동을 막을 수 있다.

결론: core (비즈니스 로직 프로세서) 도 go 언어로.

Channel 보다는 lock. (Go 언어 개발자가 expressive 한걸로 쓰라고 했음) (Lock이 다른 언어에서는 보통 성능을 크게 위협하지만, go 언어에는 큰 문제가 안된다.)

“당신만이 알고 있는 숨겨진 비밀을 공유했다면, 상대방은 이제 공모자가 된 것이다. 톨킨이 《반지의 제왕》 에 썼던 것처럼 말이다.

길은 계속해서 이어지네.

길이 시작된 문에서부터.

인생은 긴 여정이다. 먼저 지나간 이들의 발자국이 찍힌 그 길은 끝이 보이지 않는다. 하지만 이야기의 뒤로 가면 또 이런 어구가 나온다.

모퉁이를 돌면 기다리고 있는 것은

새로운 길이거나 비밀의 문.

오늘 그 길을 지나쳤지만

내일은 이 길로 올지도 모르지.

그리고 숨겨진 길을 따라

달까지, 해까지 갈지도 모르지.

길이 끝없이 이어질 필요는 없다. 숨겨진 길을 따라가라.

” (P141)

Advertisements

One thought on “차세대 실시간 게임서버 엔진 Century 설계 설명

  1. Pingback: 차세대 Go언어 게임서버 Century 뼈대 | coolspeed

댓글 남기기

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s