SMARTSTUDY 에서 몬스터 슈퍼 리그를 개발하면서 빠른 개발 진행을 위해 선택했던 Python 게임 서버, '잘 되면 다시 만들지 뭐'라는 생각에서 시작했지만 다시 만들 일은 영원히 오지 않았습니다... Python으로 게임 서버를 만들었을 때 사용한 것은 무엇인지 또 실제 오픈 했을 때 서버는 안녕했는지 알아봅니다.
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)Esun Kim오픈 소스 Actor Framework 인 Akka.NET 을 통해 온라인 게임 서버를 어떻게 구현할 수 있는지를 설명합니다. Actor Model 에 대한 기본 이해부터 Scale-out 가능한 게임 서버 구축까지 전반적인 내용에 대해 알 수 있습니다. 설명을 위해 클라이언트는 Unity3D 를 사용할 예정입니다.
NDC 11 자이언트 서버의 비밀승명 양2011 NDC(Nexon Developers Conference)에서 발표한 마비노기 영웅전(미국명 Vindictus)의 자이언트 서버 아키텍처에 대한 슬라이드입니다. 게임 서버의 분산 서비스 아키텍처를 바닥부터 만들어낸 과정과 결과에 대한 내용을 담고 있습니다.
데이터분석 기반 게임봇과 작업장 탐지 (NDC 2017)Korea University거래로그, 파티플레이 로그, 전투 및 사냥 로그 등 서버단에 남는 빅데이터를 분석하여 게임봇 또는 작업장을 탐지하는 기법들은 최근 학계 및 게임업계에서 다양하게 연구되어 현업에 적용되고 있다.
본 강의에서는 이러한 데이터마이닝 기반의 탐지기법들 중에서 최근에 활발히 연구되고 있는 유저들의 행위분석과 프로파일링 기법을 이용한 봇탐지 기법에 대해 소개한다.
이를 위해 다양한 대형 MMORPG 의 실제 로그 데이터를 이용하여 분석을 수행하였으며, 정상적인 유저와 게임봇 유저간 뚜렷이 다른 패턴을 보이는 특징값 (feature) 들을 발굴하였다. 선정된 특징들을 토대로 정상적인 유저와 게임봇 사이의 차이점을 분석한 사례를 설명하고, 범용적으로 활용할 수 있는 프로파일링 기법을 설명하도록 한다.
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기Jaeseung HaNDC 2017 발표 슬라이드
시연 영상 링크: https://youtu.be/e9Tv3jkmqKk
게임 내 정보를 추가 구현이나 패치 없이 실시간으로 수집할 수 있다면 어떨까요? 이런 아이디어를 실제로 가능하게 구현한 NEXON ZERO 발표 슬라이드 입니다.
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버Heungsub LeeNDC14에서 발표한 "[야생의 땅: 듀랑고] 서버 아키텍처" 세션의 슬라이드입니다.
슬라이드에 설명이 많지 않은데, 디스이즈게임에서 발표 내용을 잘 정리해주었습니다. 기사도 함께 보시면 좋을 것 같습니다.
http://www.thisisgame.com/webzine/news/nboard/4/?n=54955
데이터분석 기반 게임봇과 작업장 탐지 (NDC 2017)Korea University거래로그, 파티플레이 로그, 전투 및 사냥 로그 등 서버단에 남는 빅데이터를 분석하여 게임봇 또는 작업장을 탐지하는 기법들은 최근 학계 및 게임업계에서 다양하게 연구되어 현업에 적용되고 있다.
본 강의에서는 이러한 데이터마이닝 기반의 탐지기법들 중에서 최근에 활발히 연구되고 있는 유저들의 행위분석과 프로파일링 기법을 이용한 봇탐지 기법에 대해 소개한다.
이를 위해 다양한 대형 MMORPG 의 실제 로그 데이터를 이용하여 분석을 수행하였으며, 정상적인 유저와 게임봇 유저간 뚜렷이 다른 패턴을 보이는 특징값 (feature) 들을 발굴하였다. 선정된 특징들을 토대로 정상적인 유저와 게임봇 사이의 차이점을 분석한 사례를 설명하고, 범용적으로 활용할 수 있는 프로파일링 기법을 설명하도록 한다.
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기Jaeseung HaNDC 2017 발표 슬라이드
시연 영상 링크: https://youtu.be/e9Tv3jkmqKk
게임 내 정보를 추가 구현이나 패치 없이 실시간으로 수집할 수 있다면 어떨까요? 이런 아이디어를 실제로 가능하게 구현한 NEXON ZERO 발표 슬라이드 입니다.
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버Heungsub LeeNDC14에서 발표한 "[야생의 땅: 듀랑고] 서버 아키텍처" 세션의 슬라이드입니다.
슬라이드에 설명이 많지 않은데, 디스이즈게임에서 발표 내용을 잘 정리해주었습니다. 기사도 함께 보시면 좋을 것 같습니다.
http://www.thisisgame.com/webzine/news/nboard/4/?n=54955
Rhea_MMO_SNG_Convergence_Server_ArchitectureRhea Strike이 PPT 자료는 2013년 4월 24일 넥슨에서 주최한 NDC 13에서 발표한 것입니다.
배포를 위해 수정된 부분들이 있으며 기술 내용의 권리는 드래곤플라이에 있습니다.
※ 2013. 4.26 폰트수정
[NDC17] Kubernetes로 개발서버 간단히 찍어내기SeungYong Oh데브시스터즈의 Cookie Run: OvenBreak 에 적용된 Kubernetes 기반 다중 개발 서버 환경 구축 시스템에 대한 발표입니다.
Container orchestration 기반 개발 환경 구축 시스템의 필요성과, 왜 Kubernetes를 선택했는지, Kubernetes의 개념과 유용한 기능들을 다룹니다. 아울러 구축한 시스템에 대한 데모와, 작업했던 항목들에 대해 리뷰합니다.
*NDC17 발표에서는 데모 동영상을 사용했으나, 슬라이드 캡쳐로 대신합니다.
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영) Tae Young Lee파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
- 파이썬 중고급 문법부터 실전 예제까지
- 국내 어떤 책에서도 다루지 않는 진짜 데이터분석 강의
- (귀차니즘에..) 소수 금융권/대기업/공기업에게만 강의된 자료
파이썬 데이터과학 1일차 - 초보자를 위한 데이터분석, 데이터시각화 (이태영)Tae Young Lee파이썬 데이터과학 - 기초 과정(1일차)
- 데이터분석, 데이터시각화
- jupyter notebook, numpy, pandas, matplotlib, seaborn
2차 과정은 따로 올리겠습니다.
문의 및 제안 : se2n@naver.com
데이터 소스 : https://github.com/sh2orc/datascience
GDB와 strace로 Hang 걸린 Python Process 원격 디버깅Youngmin KooPython 프로그램을 디버깅하실 때 어떤 툴을 사용하시나요? 아무래도 가장 많이 사용하고 계신 툴은 PyCharm이 아닐까 싶습니다. PyCharm은 JetBrains에서 만든 GUI 환경에서 사용할 수 있는 Python IDE입니다.
PyCharm은:
로컬 컴퓨터에서 디버깅 모드로 (PyDev로) Python 프로세스를 실행할 수 있습니다.
로컬 컴퓨터에서 실행 중인 Python 프로세스에 Attach해 디버깅할 수 있습니다.
원격 서버에서 디버깅 모드로 (PyDev로) Python 프로세스를 실행할 수 있습니다.
하지만 원격에서 동작하고 있는 프로세스에 디버거를 Attach하는 기능은 제공하지 않고 있습니다. 또한 Python 디버깅 모듈인 pdb를 사용하여도 동작하고 있는 프로세스에 Attach하는 것은 지원하고 있지 않습니다.
로컬 환경에서는 문제 없이 돌아갔던 Python 프로그램이 원격 서버에서는 아무런 로그 없이 멈춰 버리는 경우 어디서부터 손을 대야 할지 정말 막막합니다. 이 때 GDB와 strace를 이용하면 어디에서 문제가 발생했는지 진단할 수 있습니다. 이 세션에서는 GDB와 strace를 이용해 디버깅하여 원격 리눅스 서버에서 Python Process가 Hang 되어 버리는 문제를 진단하고 해결했던 경험을 공유하려고 합니다.
2. 목표
• 몬슈리의 Python 게임 서버에 사용된 각종 라이브러리와 서비스 공유
• 몬슈리의 사례를 통해서 Python 으로 게임 서버를 만들 때 개발자의 의사결정에
도움이 되는 것
• 경험의 공유를 통한 “Python 은 느리지만 빨라”를 전파
몬스터 슈퍼 리그를 개발하면서 빠른 개발 진행을 위해 선택했던 Python 게임
서버, '잘 되면 다시 만들지 뭐'라는 생각에서 시작했지만 다시 만들 일은 영원히
오지 않았습니다... Python으로 게임 서버를 만들었을 때 사용한 것은 무엇인지
또 실제 오픈 했을 때 서버는 안녕했는지 알아봅니다.
from https://ndc.nexon.com
3. 목차
1. 몬슈리 Python 게임 서버는 안녕하십니까?
2. 몬슈리 Python 게임 서버
3. 안녕하기 위한 노력
4. 몬슈리 Python 게임 서버는 안녕했나요?
5. Python 게임 서버
7. Python 2.7.11
• 개발 및 유지 보수가 편해야 함
• 다양한 라이브러리와 클라우드 서비스를 지원
• 패키지 관리가 잘 되어야 하고 의존성 문제가 없어야 함
• 서버의 퍼포먼스보다 개발자의 퍼포먼스가 중요
• 이미 사내 backend 개발에 Python 을 사용하고 있음
• 몬슈리 개발팀에 적합한 언어
10. 기본적인 선택들
• Lightweight Web Application Framework - Flask
• ORM - SQLAlchemy
• KeyValue DB - Redis
• Asynchronous Tasks - Celery
11. 게임 서버는 Flask
• Protobuf message 기준으로 decorator 를 통한 routing
@route('ReqUserLogin')
def userLogin(reqUserLogin):
…
@app.route('/api', methods=['POST'])
req = request_pb2.Request.FromString(reqBody)
…
db_begin()
try:
rsp = handle(req)
db_commit()
except:
db_rollback()
finally:
db_end()
return rsp
12. 게임 서버는 Flask
• Error 처리
• Status code=500
• 명백한 서버 오류, 클라이언트에서 “재시도” or “재접속” 팝업
• Status code=200 & Error Code 를 나누어 사용
• 서버와 클라이언트의 데이터가 맞지 않는 상황으로 판단
• 오류 메시지 노출 후 계속 진행 or “재접속”
13. 게임 서버는 Flask
• 클라이언트의 Retry 대비
• Response 를 Redis 에 보관 후 동일한 Request 에 대해서 Response 를
바로 Return
• 2 회 이상 발생시 Status Code=200 + Error Code return
14. DB 는 SQLAlchemy
• Alembic 을 이용한 DB Migration 지원
• Multi DB 지원
• Auto Commit 사용하지 않음
• 모든 User Request 에 대해서 Transaction 사용
• Request 처리 중 서버 오류나 데이터 오류의 경우 Rollback
• 오류 없이 로직이 처리된 경우 Commit
15. 데이터는 Protocol Buffers (ProtoBuf)
• Message 파일을 정의하고 Compile
• Python , 3rd party c# 지원
• optional
• enum
• type checker
• python 을 위한 C++ 구현체 지원
message MsgUserItem
{
optional fixed32 item_uid = 1;
optional uint32 item_count = 2;
}
enum MonsterStatType {
MS_None = 0;
MS_Attack = 1;
MS_Defence = 2;
MS_Heal = 3;
MS_Balance = 4;
MS_Hp = 5;
}
16. 랜덤은 NumPy
• Python Random 보다 빠르고 더 좋은 결과를 주지 않을까?
• 막연한 기대로 선택
• 그러나 실제로는…
20. 랜덤은 NumPy
• randint 는 성능 차이가 10배 정도 나지만 random 은 차이 없음
• python.random.choice vs numpy.random.choice
>>> import random, numpy
>>> a = ['Miho', 'Anu', 'Leo', 'Lily', 'Seiren', 'Sura', 'Persephone', 'Nightmare']
>>> random.choice(a)
'Anu'
>>> numpy.random.choice(a, 11, p=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.2]).tolist()
['Lily', 'Anu', 'Persephone', 'Sura', 'Seiren', 'Sura', 'Sura', 'Persephone', 'Anu', 'Lily', 'Anu']
random Time (s)
random.random 0.077058
numpy.random 0.078524
python.randint 1.091723
numpy.randint 0.102347
21. 서버간 연결은 requests
• Python 용 HTTP 라이브러리, 주로 서버간 통신에 사용
• Timeout 설정
• Timeout 설정하지 않으면 exception 없는 한 무한 대기
“Without a timeout, your code may hang for minutes or more.”
• User request 에서 서버간 request timeout : 3~9s
• Scheduler 에서 서버간 request timeout : 30s
>>> r = requests.post(url, json=json, timeout=3, headers=headers)
22. Quest 는 Blinker 로
• Blinker : Signal(Event) Broadcasting 라이브러리
• Signal(Event) 을 send 하는 즉시 Receiver 에게 데이터 전달
• Quest 에서 사용한 방법
• Quest 종류 별로 미리 Custom Name Signal(Event) 정의
• Signal(Event) 별로 1개의 Receiver 를 connect
• 서버 로직에서 Quest 체크가 필요한 경우 Signal 을 send
23. Quest 는 Blinker 로
# signals.py
import blinker
namespace = blinker.Namespace()
summon = namespace.signal('summon')
…
# main.py - subscribe summon signal (event)
import signals
…
@signals.summon.connect
def check_summon(sender, user, **kwargs):
quests = find_quests(user, main_condi=data_pb2.MsgQuest.Summon, **kwargs)
if quests:
add_extra_response(quests_inc_count(user, quests))
…
# summon 에 대한 각종 조건 체크 후 퀘스트 진행상황 업데이트
signals.summon.send(user=user, monster=new_monster, summon_mon_egg=True)
…
24. 게임 데이터는 gdata 로
• 기획팀에서 사용하는 data 는 google sheet 로 관리
• data sheet 를 protobuf serializing 하여 file 로 저장
• 116 google sheets (11 files) serializing = 15s
$ ls
joongom staff 644797 3 16 13:37 string.zh-tw.pb
joongom staff 660737 3 16 13:37 string.zh-cn.pb
joongom staff 1676594 3 16 13:37 string.th.pb
joongom staff 739756 3 16 13:37 string.pt.pb
joongom staff 807264 3 16 13:37 string.ko.pb
joongom staff 881384 3 16 13:37 string.ja.pb
joongom staff 763874 3 16 13:37 string.it.pb
joongom staff 794710 3 16 13:37 string.fr.pb
joongom staff 764741 3 16 13:37 string.es.pb
joongom staff 707415 3 16 13:37 string.en.pb
joongom staff 757907 3 16 13:37 string.de.pb
joongom staff 3413470 3 16 13:37 gamedata.pb
25. 게임 데이터는 gdata 로
• 수정 전 데이터와의 diff 를 확인하는 별도의 툴이 필요
• 데이터 배포 전 반드시 확인하는 절차를 진행
• protobuf 를 dict 으로 변경하여 diff
• protobuf-to-dict : https://github.com/benhodgson/protobuf-to-dict
$ data_diff.py 610 620
removed mon.cocomaru.tree.1 substages uid:stage.01.05 normal_display_mons index:2
removed mon.slimeb.water.1 substages uid:stage.01.05 normal_display_mons index:3
modified mon.jackolittlew.light.3 --> mon.slime.tree.3 substages uid:stage.01.02 hell_display_mons index:0
modified mon.jackolittle.dark.3 --> mon.slimeb.water.3 substages uid:stage.01.02 hell_display_mons index:1
modified mon.jackolittle.dark.3 --> mon.squir.tree.3 substages uid:stage.01.02 hell_display_mons index:2
added mon.slime.tree.3 substages uid:stage.01.02 hell_display_mons index:3
26. Python Exception 은 Sentry
• Sentry is a modern error logging and aggregation platform.
server.py
...
sentry = Sentry(dsn='http://public_key:secret_key@example.com/1')
def create_app():
app = Flask(__name__)
sentry.init_app(app)
return app
...
try:
...
except:
sentry.captureException(extra={’User’: userData, ‘Request’: requestData})
28. nginx + uWSGI + supervisor
• Flask
• WSGI 표준 지원, uWSGI 로 nginx 연결
• nginx
• 각종 성능에 도움이 될 수 있는 옵션들 적용
• 배포 서버에서만 사용 (개발할 때는 flask run server)
• Supervisor
• Process 컨트롤 시스템
31. CBT #1 기본 서버 구성
• Game Server & Game Tool Server
• Flask app x n
• Nginx worker x m
• Redis x 1
• Celery x 1
• AWS RDS(MySQL) x 1
• AWS ElasticCache(Redis) x 1
• AWS ELB x 2
• AWS EC2 x 3
• AWS ECS
• Game server service (2 task)
• Game tool service (1 task)
34. 안녕하세요.
• 퍼블리셔 : 안녕하세요. “퍼블리셔” 입니다.
• 개발자 : 안녕하세요.
준곰이라고 불러 주세요. ☺
2003 년부터 NEXON, NCSOFT, NEOWIZ 에서 게임을 만들었습니다.
대부분 서비스를 종료했습니다만… 왠지 이번에는 느낌이 좋네요.
그리고 2010년 부터 스마트스터디에서 핑크퐁 앱과 각종 게임들을 만들었습니다.
CTO 를 하고 있는 몬슈리 개발자 박준철 입니다.
35. Sharding 해주실 수 있나요?
• 개 : 네, Table 마다 다른 DB 를 지정할 수 있습니다.
• 퍼 : 1개의 Table 을 n개의 DB에 저장하는 Sharding 을 해주셔야…
• 개 : ORM 쓰고 foreign key도 썼는데… T_T
36. DB Sharding
• CBT #1 에서 DB CPU 사용률이 높은 것을 확인
• ‘퍼’ 님이 100배 많이 유저님들을 모시고 올 것이니…
A. Foreign key 를 삭제
B. Flask-SQLAlchemy 코드를 수정 Sharding 구현
a. 1 DB = Shard DB x n
b. User 기준으로 GameDB sharding
c. User 별 Shard DB 정보 - 1 개의 CommonDB
37. DB Sharding
SQLALCHEMY_BINDS = {
'common': 'mysql://msl:msl@localhost/MSLCommonDB',
'game': 'mysql://msl:msl@localhost/MSLGameDB1',
'log': 'mysql://msl:msl@localhost/MSLLogDB1',
'clan': 'mysql://msl:msl@localhost/MSLClanDB',
}
SQLALCHEMY_BINDS = {
'common': 'mysql://msl:msl@localhost/MSLCommonDB',
'game': ['mysql://msl:msl@localhost/MSLGameDB1',
'mysql://msl:msl@localhost/MSLGameDB2'],
'log': ['mysql://msl:msl@localhost/MSLLogDB1',
'mysql://msl:msl@localhost/MSLLogDB2'],
'clan': 'mysql://msl:msl@localhost/MSLClanDB',
}
use commondb;
insert into db (db_idx, host, db_name, db_type, world_idx, shard_no, use_yn, reg_date)
values (11, 'localhost', 'gdb1', 'U', 1, 1, 'Y', now());
insert into db (db_idx, host, db_name, db_type, world_idx, shard_no, use_yn, reg_date)
values (12, 'localhost', 'gdb2', 'U', 1, 2, 'Y', now());
insert into db (db_idx, host, db_name, db_type, world_idx, shard_no, use_yn, reg_date)
values (21, 'localhost', 'logdb1', 'L', 1, 1, 'Y', now());
insert into db (db_idx, host, db_name, db_type, world_idx, shard_no, use_yn, reg_date)
values (22, 'localhost', 'logdb2', 'L', 1, 2, 'Y', now());
• Shard DB 정보는 commondb.db 에서 관리
38. DB Sharding
> show create table commondb.account;
-----------------------------------------------------
…
`user_id` bigint(20) NOT NULL,
`gamedb_shard_no` smallint(6) NOT NULL DEFAULT `1`,
`logdb_shard_no` smallint(6) NOT NULL DEFAULT `1`,
…
------------------------------------------------------
• user 의 game, log db 를 sharding
• gamedb, logdb 의 sharding 을 구성하는 DB 개수가 다를 수 있음
• gamedb_shard_no, logdb_shard_no 정보를 account 에 추가
39. DB Sharding
db.set_default_shard(current_app, 'game', gamedb_shard_no)
db.set_default_shard(current_app, 'log', logdb_shard_no)
q = User.query.set_shard(gamedb_shard_no).filter_by(id=user_id)
• User Request 처리는 대체로 user 의 data 에 접근하므로 shard_no 고정적
• default_shard_no 지정 기능 추가
• 친구 data에 접근하는 경우 다른 shard_no 를 사용하는 경우 발생
• Query 에 shard 지정 기능 추가
40. Redis CPU 사용률이 너무 높은데요?
• 개 : 성능 좋은 인스턴스로 바꿔서 서비스 해줘요.
• 퍼 : redis 는 싱글 스레드라…
• 개 : 아…
• 퍼 : redis 도 Sharding 해주시면 좋겠습니다 ☺
• 개 : 아…
41. Redis Sharding
• https://redis.io/topics/benchmarks
• “Redis runs slower on a VM compared to running without virtualization
using the same hardware. If you have the chance to run Redis on a
physical machine this is preferred. …”
• virtualized vs bare-metal servers (without pipeline)
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz Linode 2048 instance
SET 122556.53 req/sec 35001.75 req/sec
GET 123601.76 req/sec 37481.26 req/sec
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
42. Redis Sharding
• Redis 저장 데이터
• User Session Token & Data ( expire=3600 )
• User Response Cache Data ( expire=3600 )
• Recommended Friends
• Friend Dungeon Data ( expire=3600 )
• Server Cache Data
43. Redis Sharding
• 1 User Request
• 1 Read User Session Token + 1 Read User Session Data
• 1 Write User Session Token + 1 Write User Session Data
• 1 Read and 1 Write Serialized Response Cache Data
• CCU 100,000 : 0.07 TPS * 100,000 * 6 = 42,000 / sec
• User data 만 User Session Token 으로 Sharding
redis_shard_no = Hash(User Session Token) % session_redis_count
44. Monitoring은 뭘로 하나요?
• 퍼 : 서버 Monitoring은 뭘로 하나요?
• 개 : Exception 나면 Sentry 가 와요. Sentry 짱
• 퍼 : Exception 안 날 때는 뭘로 봐요?
• 개 : 잘 돌고 있겠죠…
• 퍼 : …
45. New Relic
• APM(Application Performance Monitoring)
• 24 시간 이내 Request 의 Transaction 분석 무료
• Database Transaction 분석 유료
• Transaction 별 분석 가능 (set_transaction_name)
…
app = newrelic.agent.wsgi_application()(app)
…
def handle(api_name, req):
…
newrelic.agent.set_transaction_name(api_name)
…
47. New Relic
• 대략 많이 비쌈 (c4.4xlarge x 25 = essentials $7,500/month)
48. Performance Test 해주세요
• 퍼 : 서버 준비할 수 있도록 Performance Test 해주세요.
• 개 : 그냥 AWS 인데 100대로 시작하면 안되나요?
• 퍼 : 네 =_=
• 개 : 아…
49. Performance Test
• nGrinder, Locust, New Relic
• Test Server
• DB : AWS RDS db.r3.large x 1 (2 Core, 16 GiB)
• Game Server : AWS ec2 c4.xlarge x 3 (4 Core, 7.5 GiB)
• Test Client
• Master : AWS ec2 c4.xlarge x 1 (4 Core, 7.5 GiB)
• Slave : AWS ec2 c4.xlarge x 6 (4 Core, 7.5 GiB)
50. Performance Test
• nGrinder
( https://naver.github.io/ngrinder )
• Java 기반, python 지원 안 함
• Master x 1, Slave x 6
• Test Request : Connection Info
• 700 TPS 까지 안정적(233 TPS/server)
• 700 TPS 이상에서는 request 가 쌓이면서
latency 급격히 증가
52. Performance Test
• Locust Bot (15 가지 주요 행동 )
• User Register, Login, Gift, Friend, Quest, Battle 등 구현
• 특정 대기 시간 후 지정된 확률로 행동(Behavior) 을 선택 후 실행
• 이전 Response 정보 반영하여 행동
53. Performance Test
• 110 TPS (36 TPS/server)
• CPU 사용률은 최대 80%
• 1 Request = 1 core 를 100ms
점유 , 4 core 는 40 TPS
54. Performance Test
• CBT 를 기준으로 1 user = 0.07 TPS
예측 동접 TPS / User TPS
40 TPS
(4 core)
80 TPS
(8 core)
10,000 0.07 700 17 대 9 대
30,000 0.07 2100 52 대 26 대
50,000 0.07 3500 87 대 43 대
100,000 0.07 7000 174 대 86 대
55. Performance Test 후
• 개 : 10 만 동접을 위해 8 core 서버를 86 대면 충분하네요.
• 퍼 : …
• 개 : 다들 이정도 쓰시죠? …
• 퍼 : …
56. Performance Improvement - Protobuf
Dockerfile
ADD protobuf-2.6.1.tar.gz /app/
WORKDIR /app
RUN
mv protobuf-2.6.1 protobuf &&
cd protobuf &&
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp &&
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2 &&
./autogen.sh &&
./configure CXXFLAGS="-DNDEBUG -O2" CFLAGS="-DNDEBUG -O2" &&
make &&
make install &&
cd python &&
python setup.py build &&
python setup.py build --cpp_implementation &&
python setup.py install --cpp_implementation &&
• Protobuf C++ 구현체 사용
• 메모리 사용량 90% 이상 절감
• 연산 속도 30배 이상 향상
• 단, float 사용 주의
58. Performance Improvement - Redis
with redis_store.pipeline() as pipeline:
try:
pipeline.hmset(name, mapping)
pipeline.expire(name, expire_time)
pipeline.execute()
• Pipeline 적용
• Avg calls 93 5
59. Performance Improvement - DB
q = db.session.query(User, UserMonster).
set_shard(gamedb_shard_no).
filter(User.id.in_(ids)).
filter(User.monster_id==UserMonster.id).
all()
for u, m in q:
…
• ORM 에 의지, Sharding
• QuerySet loop .all()
• Foreign key join
• Avg calls 23 3
60. Performance Improvement - DB
• 기존의 경우 Friend 를 1회 select 하여 n개의 data를 가져옴
• n 번 commondb 의 Account 를 select
• n 번 gamedb 의 User 를 select
• n 번 gamedb 의 UserMonster 를 select
67. Performance Improvement - PyPy
• 다양한 패턴의 request 처리에 대해서는 성능이 빠르게 올라가지 않음
• 초반 느린 부트스트래핑(Bootstrapping) 단계로 인해 Latency 증가
• Protobuf C++ 구현체를 사용할 수 없음
• 높은 메모리 사용량
68. Performance Improvement - PyPy
• 다양한 패턴의 request 처리에 대해서는 성능이 빠르게 올라가지 않음
• 초반 느린 부트스트래핑(Bootstrapping) 단계로 인해 Latency 증가
• Protobuf C++ 구현체를 사용할 수 없음
• 높은 메모리 사용량
• 사용 포기
69. Performance Improvement - Result
• Protobuf C++ 구현체로 변경
• Redis pipeline 사용
• Database Query 최적화
• 8 Core 15 GiB x 50• Python CPU 점유 시간
• 100ms 50ms
• 4 Core 7.5 GiB x 174
75. 생각해본 것
• 처음부터 PyPy 를 고려한 설계를 했다면?
• 처음부터 Sharding 을 고려했다면?
• MySQL 이 아닌 다른 DB 를 사용했다면?
• Cython 으로 일부를 정적 컴파일하여 사용했다면?
• Protobuf 보다 빠른 data serialization 이 있다면?
76. Python 게임 서버
• 각종 라이브러리와 클라우드 서비스를 쉽게 이용할 수 있다.
• 느리지만 빠르다.
• Python 게임 서버 안녕합니다. 걱정 안하셔도 됩니다.
77. • 글로벌 콘텐츠 기업
• 창업 8년차
• 직원 수 143명
• 대표 콘텐츠
• 핑크퐁
• 상어 가족
• 몬스터 슈퍼리그