[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버

Post on 18-Dec-2014

15394 Views

Category:

Software

16 Downloads

Preview:

Click to see full reader

DESCRIPTION

NDC14에서 발표한 "[야생의 땅: 듀랑고] 서버 아키텍처" 세션의 슬라이드입니다. 슬라이드에 설명이 많지 않은데, 디스이즈게임에서 발표 내용을 잘 정리해주었습니다. 기사도 함께 보시면 좋을 것 같습니다. http://www.thisisgame.com/webzine/news/nboard/4/?n=54955

Transcript

이흥섭

왓 스튜디오 / 넥슨코리아

<야생의 땅: 듀랑고>

서버 아키텍처SPOF 없는 분산 MMORPG 서버

이흥섭 sub@nexon.co.kr

• 주로 게임 서버 프로그래밍

• 현재 <야생의 땅: 듀랑고>

• <카트라이더 대시 & 코인러시> - 페이스북, 카카오 MO 게임

• <한글라이즈>, <블라> 등 웹 서비스

• 틈틈이 오픈소스 활동 https://github.com/sublee

<야생의 땅: 듀랑고>?

모바일오픈월드

MMORPG

실시간 동기화

영속성

심리스

단일세계

설계 목표와 원칙

높은 가용성High availability

연간 다운타임 5.5분 미만

가용율 99.999%

http://status.aws.amazon.com/

https://status.github.com/

https://developers.facebook.com/status/

신축성Elasticity

AWS

설계 목표

• 높은 가용성• 신축성

無SPOF

=

=

=

=

SPOFSingle point of failure

라우터

웹서버

웹서버

웹서버

라우터

웹서버

웹서버

웹서버

라우터

웹서버

웹서버

웹서버

캐시

게임서버

중앙서버

로그인서버

DB

캐시

게임서버

중앙서버

로그인서버

DB

캐시

게임서버

중앙서버

로그인서버

DB

캐시

게임서버

중앙서버

로그인서버

DB

캐시

게임서버

중앙서버

로그인서버

DB

캐시

게임서버

중앙서버

로그인서버

DB

부하가 몰려서 이중화

캐시

게임서버

중앙서버

로그인서버

DB

이중화하기 쉬우니까 이중화

캐시

게임서버

중앙서버

로그인서버

DB

고사양무결점

인프라

고사양무결점

AWS

모든 서버는분명히 죽는다

빠짐 없이 이중화

無SPOF

✔ 부하 분산✔ 중요성 분산

=

✔ 부분적 장애

• 가정: 모든 서버는 죽는다.• 無SPOF• 부분적인 장애 허용

설계 원칙

파이썬

• 코드의 양 ≒ 생각의 양• 방대한 오픈소스 커뮤니티

✔ 높은 생산성

• 느린 연산• 멀티스레딩 미흡

✔ C로 대체✔ I/O가 더 중요

gevent코루틴 기반 통신 라이브러리

동기(Synchronous) I/O

value = db.load('durango')

print value 스레드 봉쇄

멀티스레딩 + 동기 I/O

스레드 개수가 동시성 한계

비동기(Asynchronous) I/O

def callback(value):

print value

db.async_load('durango', callback)

JavaScript로

db.load('durango', function(value) {

print(value);

});

db.load('durango', function(value) {

db.load(value, function(value2) {

db.load(value2, function(value3) {

print(value3);

});

});

});

db.load('durango', function(err, value) {

if (err) {

// 예외처리

} else {

print(value);

}

});

코루틴 I/O

value = yield db.load('durango')

print value

코루틴?

서브루틴main() func()

함수 호출

return/raise

코루틴main()

함수 호출

return/raise

yield

next

func()

hub() send() recv() sleep()

코루틴 I/O

value = yield db.load('durango')

print value

다른 일

try:

value = yield db.load('durango')

except NotFound:

# 예외처리

else:

print value

try:

value = yield db.load('durango')

except NotFound:

# 예외처리

else:

print value

geventI/O 함수들이 암시적으로 yield하도록 패치

gevent I/O

value = db.load('durango')

print value

다른 일

암시적 yield?

동기 I/O 방식으로작성된 라이브러리를모두 그대로 사용 가능

gevent 마이크로스레드

• 직관적 코드• 오픈소스 커뮤니티• gevent

파이썬

서버 간 통신

=

P2P 네트워크

홀펀처

ØMQN:N 다중 연결 소켓

… … …

… …

작업큐 패턴

15 3

24

아무나 받아라

Pub/Sub 패턴

12

2 13

3

다 받아라

Pub/Sub 패턴

123 구독

=

… ……

MMORPG 분산처리

영속성 휘발성

영속성 휘발성DB게임서버

영속성 휘발성DB

게임서버

MMORPG 분산처리

분산 데이터베이스

RDBMS?

• 분산하기 난해• 느린 속도

• ALTER TABLE

CouchbaseNoSQL 키-밸류 저장소

• 무중단 확장/축소• 부분적 장애 허용• 스키마 없음

빠른 속도 – 메모리 우선

App

DB

메모리에 저장 디스크에 저장 복제 노드에 저장

OK

✘ 내용 검색

• Elasticsearch 연동• N1QL

SELECT studio FROM games AS game

WHERE game.name = 'durango'

• Couchbase• 높은 가용성• 신축성

분산 데이터베이스

MMORPG 분산처리

분산 게임서버

단일세계

심리스

영속성

상호작용 가능성 ∝

높음

낮음

1거리

1m

100m

게임서버 A 게임서버 B

1m

100m

게임서버 A 게임서버 B

심 심심

지역 A지역 B 지역 C 지역 D

지역분할

게임서버 A 게임서버 B

채널 A 채널 B

채널링

✘ 지역분할✘ 채널링

무선 통신 < 서버 간 통신

게임서버 A 게임서버 B

가까우면 모아주기

✘ 필수 ✔ 권장

1m

100m

게임서버

게임서버의 시야

게임서버

개체 시야 합 = 게임서버 시야

게임서버

Pub/Sub 패턴

123 구독

= Pub/Sub 채널

게임서버

구독

구독

구독

게임서버

발행

발행

발행

발행

A

B

A

B

동기화

로그인서버

1/n

게이머들이 돌아다니면…

• 세계를 유연하게 분할• 게임서버의 시야• 필요한 정보만 동기화

분산 게임서버

MMORPG 분산처리

개체 간 상호작용

if B.is_enemy_of(A):

B.damaged(A.strength)

A B공격

동기화

if B.is_enemy_of(A):

B.damaged(A.strength)

A B공격 Bor

액세서

상태를 읽기만 하는 메소드

B.is_enemy_of()

뮤테이터

상태를 변경하는 메소드

B.damaged()

액세서 뮤테이터

✔✔

✔ ✘

.is_enemy_of().is_enemy_of()

액세서 로직액세서 로직

.damaged().damaged()

뮤테이터 로직

RPC 요청

.???().???()

• 고스트로 추상화• 액세서는 분산• 뮤테이터는 집중

개체 간 상호작용

현 위치

DB게임서버

로그인서버

ELB

✔ 부분적 장애 허용✔ 무중단 확장/축소

✔ 높은 가용성✔ 신축성

• 잠시 후, 진선웅

<야생의 땅: 듀랑고>의 절차적인 섬 생성 기법

• 내일 9:50, 이은석

온라인 게임의 창발적 게임플레이 디자인

• 내일 15:55, 박영준

<야생의 땅: 듀랑고>의 좌충우돌 개발 과정

질의응답

국내에 AWS 데이터센터가 없는데한국에서 상용 서비스를 돌리기에는 느리지 않을까요?

한국 IDC에 비해 통신 속도는 다소 느립니다. 하지만 게임 디자

인 차원에서 느리고 불안정한 무선 통신을 가정하기 때문에 사

용자 경험에 영향을 주지는 않을 것 같습니다.

(요즘 일본 EC2와의 핑 지연시간은 40ms 정도 나옵니다.)

Q.

A.

gevent를 사용해도 C 라이브러리의 I/O는 봉쇄될텐데, DB 드라이버 등의 선택에 제약이 많을 것 같습니다.

스레드를 봉쇄하는 C 라이브러리는 사용하지 않습니다.

Python 오픈소스 커뮤니티에서 적절한 도구를 찾는 데에 큰 어

려움은 없었습니다. Couchbase 드라이버의 경우 gevent를

지원하는 Python 라이브러리가 개발되어 있습니다.

Q.

A.

특정 서버에 부하가 몰려그쪽으로 보내던 RPC가 지연되면 어떻게 되나요?

보내는 쪽에 RPC 실패 또는 타임아웃이 감지됩니다. 그 예외를

잡아 게이머에게 장애 상황을 통지합니다.

Q.

A.

MMORPG는 트랜잭션 처리가중요할 것 같은데 어떻게 하셨나요?

Couchbase에서 문서 내 트랜잭션은 보장할 수 있고, 문서 간 트랜잭션은 보장할 수

없습니다.

예를 들어 아이템을 습득하는 경우, 아이템을 DB에 추가하고 인벤토리에 아이템

DB를 추가해 DB 상에서 갱신합니다. 두 작업은 원자적으로 실행될 수 없고 둘 중

하나가 실패할 수 있습니다. 이 경우엔 아이템 추가가 우선 끝나면 인벤토리에 연결

하는 방법으로 로직을 작성합니다. 추가됐으나 연결되지 않아서 미아가 되는 아이템

도 생길 것입니다.

이런 방식만으로는 모든 케이스가 해결되지 않는 다는 것을 알고 있고, 높은 일관성

과 복구 가능성을 확보하기 위해 노력 중입니다.

Q.

A.

뮤테이터 호출 시 액세서에서 확인한 상태가이미 변경되었을 경우 문제가 생길 수 있지 않나요?

LBYL (Look before you leap) 방식과 EAFP (Easier to ask for forgiveness

than permission) 방식이 있습니다. 전자는 할 수 있는지 먼저 확인하고 하는 것,

후자는 일단 한 후 문제 생기면 예외처리 하는 것입니다. 분산환경에서 LBYL 방식은

실패할 확률이 높지만 EAFP 방식은 그렇지 않습니다. 그래서 저희는 EAFP 방식을

주로 사용하고 있습니다.

질문하신 케이스의 경우, 액세서의 결과가 시시각각 변하는 거라면 일단 뮤테이터를

호출하고 오류가 발생했을 때 예외처리 합니다.

Q.

A.

top related