대용량 서비스를 개발하고 로드 테스트를 수행하기

이 아티클은 대용량 웹 서비스개발을 위한 표준적인 해결방법을 잘 정리한 문서는 아니며

특수한 게임서비스의 플랫폼 변화과정에서 개인이 진행한 대용량 웹서비스를 위한 아케텍쳐 설계를

포함한 구현과정과 로드테스트에 대응한 경험에 대한 기술입니다.


일반적인 웹서비스는 아니며, 국내 단일 서비스에 비해 글로벌 웹서비스는 더 많은 문제를 해결해야하며

대규모 처리를위해 고려해야 했던 특징은 다음과 같습니다.

  • 글로벌 트래픽이 높았던 합법운영 겜블러 게임 서비스 ( cereus poker network / bodog / bovada ) -다중언어,인터넷품질이 각각 다른 지역대응
  • Display 에서 1의 의미는 실제 1$ 이며, 게임액션중 이 금액의 전이가 발생합니다. 이 액션은 사용자당 분당 10회 이상이 발생할수 있습니다. - 단순하게 1일 1구매를 하는 패턴의 일반 예약시스템과 금액 트랜잭션의 규모와 방식이 다릅니다.
  • 토너먼트를 포함하여 수천개의 다양한 게임테이블이 존재하며 랭킹,테이블당 이용여부및 플레이어수등 모든것은 3초이내에 변경사항을 실시간 반영을 하여야합니다. -클릭액션이 상대편에게 전달되는 실시간 처리기능을 포함하여 동시 5천명이상의 토너먼트 기능을 실시간으로 운영한다란것은 10년전에 있었던 기능이며 현재 경험있는 온라인서비스에서도 구현하기 어려운 기능중에 하나입니다.
  • 모든 관리자모드에서 변경사항은 실시간접속중인 모든 사용자에게 정확하게 전달되어야함 -로그인 API를 포함하여 DB암호변경이 실시간 가능했으며 바꾸는것만으로 신규적용이 되고 실시간 리로그인이되었음
  • 업데이트시 무중단 서버기능이 반영되어있습니다. 사용자는 게임을 계속 진행중이지만, 서버가 업데이트를 하더라도 연속된 게임진행이가능합니다. -복잡한 서비스를 무중단으로 운영해야함
  • 로그인은 접속제한 체크및, 다중기기이용체크를 포함하여 블락된 사용자의 모든 장치를 막아야하는 기능이 포함되어 있습니다. - 복잡한 로그인 기능

개요

Native c++ 방식으로 통신을하면서 운영중인 게임서버 에서

웹서비스 패러다임으로 변경되면서 웹서비스에 적합하 JAVA언어를 사용하기로 결정하고

경험있는 JAVA 웹서비스 개발자들을 영입하면서 프로젝트를 진행 


신규 플래폼:

  • AKKA-Actor(Scala and JAVA)
  • Atmosphere(Websocket)
  • PlayFrameWork(Micro webservice
  • JNI ( JAVA ↔ c++ ) : 기존 c++과 통신해야하는 부분은 JNI로 커버


설계

단일 병목지점 포인트( Aux-DB,DC)는 항상 존재를 하며 단일지점의 요청을 얼마나 줄이고 분산되는 시스템에 나눠서 일을 시킬수 있느냐가 설계포인트

DB를 호출하는 대부분의 병목 단일지점은 Aux라고 네이밍 지어진 장비, DB의 호출을 책임지고 있는 장비라고 보시면 됩니다. 

애시당초 중앙 DB의 병목 현상을 , 서비스를 스케일아웃한다고 해결할수 있는것이아닙니다.

서비스 패러다임이 변경되어, 경험있는 다양한 웹서비스 개발자들을 영입을 시도하던 때였으며 그들이 할수 있는 것은 SQL을 호출하여 전달하는것이

다였으며 아무런 도움이 되질 못하였습니다. 그렇다고 고성능 쿼리를 짤수 있는 것도 아니였으며

데이터 중심(쿼리)으로 진행된 몇가지 신규 게임서비스를 망쳐놓기까지 했습니다. 100명의 실시간 게임 사용자가 버튼을 통해 끊임없이 트랜잭션을

유도한다란 의미는 일반적인 웹서비스에 10배가 넘는 사용자가 발생하는 트랜잭션의 양일수도 있으나 그것을 구분하지 못하고 실제로

가장 최신의 DB장비로 그것을 해결하지 못하였습니다.

우리는 이때 웹서비스를 공부하며, RestAPI를 ForkInjoin하여 효율적으로 병렬처리하는 방식과 가비지컬렉터가 작동하는 방식을포함하여

웹소켓을 최적화하고 그것을 분산처리하는 방법등 심지어 고성능 Json 사용방법 , 처음 접해보는 JAVA의 형변환 최적화등을 스스로 연구하기로 하였습니다.

이 당시 어떠한 언어를 사용하던 웹서비스 개발자의 수준이 SQL결과문을 전달하는것이였으며 , 경험있는 웹서비스 개발자를 영입하여

게임 웹서비스에 참여 시도한것은 대부분 실패하였습니다.


간단한 로컬 용어정리

DC/TMS : 데이터센터로 생산된 정보중 중요한 정보를 DB에 적는 역활,DB가 뻗어도 작동되게 설계가 되어집니다.

LS/TLS : C++로 작성된 로그인/로비게임들을 관리하는 서버

LobbyAPI : 모던웹 규격에 맞게 웹인터페이스를 제공하고, 백엔드 메시지처리는 C++패킷을 통해처리

로비 : 로그인처러 게임내결제를 비롯하여 수만명 사용자가 게임에 접근하기위한 게임테이블을 정보를 제공



REST 설계

기존 실시간 처리되는 저수준 패킷들을 모두 Rest화를 합니다. 기존 비동기 처리를 응답을 기다려야하는 Rest와 성능을 유지하면서 

설계를 해야할지가 고민이며 웹프레임워크의 튜닝요소를 깊게 살펴보는것에서부터 시작합니다.

이러한 API는 DB설계에 맞추어 이루어지는것이 아닙니다. 우리가 원하는 스펙을 API를 통해 구체적으로 명시화하고

DB는 단지 그 과정에서 저장해야할것을 저장하는 Persitence의 역활만 하면되었기때문입니다.

또한 데이터를 먼저 정의한후 어플리케이션을 설계하려고하면, 구조적인 불일치뿐만아니라

게임웹서비스는 아무것도 못하고 망할 가능성이 높으며 실제로 그러했습니다.

한가지 특징을 살펴보면, 비 연결 웹 세션을 어떻게 최소한으로 유지 할까란 고민으로 KeepAlive를 설계하고 실제 DB호출에 발생하는

비용을 90%이상줄였으며 API설계시 고려 했었다란 점입니다. 

URL : https://www.playframework.com/documentation/2.1.0/ThreadPools


로비처리 웹소켓 설계



실시간처리에 있어서 Rest는 적합하지 않으며 고성능 처리에 한계가 있습니다.

Rest호출 횟수를 줄이기위해 웹소켓을 보조적으로 사용을 하였습니다.

보통 동접3만에 특정기능만 초당 천번이상의 Request가 필요한 정보다라고 측정이되면

이것을 RestAPI화 하여 DB를 호출하여 해결한다란것은 무모한짓입니다.

웹소켓으로의 전환이 필요하며 캐싱은 필수이며   처음 스냅샷데이터는 Rest로 처리하고,

이후 작은단위 필요한 녀석만 지속적 갱신처리화도 필요한 부분입니다.


고성능 통신 라이브러리인 자바-네티를 사용하는 Atmosphere를 사용하였습니다.

고성능처리를 위해서는 여러가지 메시지기법과 같이 사용해야합니다.

URL : https://github.com/Atmosphere


성능튜닝


설계된대로 개발이 완료되었으며, 기존 사용자+30% 를 모두 커버할수 있는지?

검증을하는것은 개발만큼 많은 시간이 소요가 되었습니다.


측정 히스토리

클라이트가 아무 액션을 하지 않는 서버의 상태의 스냅샷을 기입합니다. 클라이언트가 아무 액션을 하지 않는다고

서버가 놀고있지는 않습니다. 사용자가 없이 흘러간 시간별 스냅샵 비교는 중요한 테스트 부분입니다.

기본적으로 백그라운드에서 작동하는 서비스가 있기때문에 0 - 1 액션의 Diff는 변화량을 측정하기가 어렵거나 미비합니다.

최소 100Action이상 사용자가 늘어나는 상황에서 비교를 하는게 의미 있는 데이터이나, 1액션 자체에 성능 문제가 있을수도 있기때문에

사소한것이라도 검증을 하는게 권장됩니다. 테스트 스크립트도 완성이되고, 1Node 테스트가 어느정도수행이되면 클러스터로

여러대를 구성하여 추가 테스트를 수행하게됩니다. 1Node 테스트때 찾지 못한 여러가지 문제를 찾게됩니다.

스케일아웃을 했다고 성능이 늘어났을것으로 기대하지만 예상외로 1Node일때 문제없는 성능부분이 스케일 아웃이되면서

예측하지 못한 많은 성능 문제가 발생하게됩니다. 오히려 병목지점을 예상하지 못하면, 단일지점 병목을 가속화시킵니다.


아무것도 하지 않는 서버의 상태측정

1액션테스트

Loading

아무액션없는 측정량

Loading

메모리사용량

Loading

메모리덤프비교

Loading



아무것도 하지 않는 서버의 상태측정은 중요한 내용입니다.

실제 로드테스트중 CPU가 틴다고 경고가 발생하여 그 문제를 찾느라 시간을 소비하였지만

최초 셋팅된 서버자체가 불필요한 서비스를 로드하면서 그냥 발생했던 문제였습니다.


성능 개선을 위한 기타 정보

가비지 컬렉트가 없는 C++에서는 메모리 해제, 큐를 통한 효율적 메모리 관련,메모리 재사용등이 성능향샹에

이슈였지만, Java/C#을 사용하면서 아무래도 가비지 컬렉트 이해에 대한 이슈로 전환되게 되었습니다.

오픈소스를 사용하는경우 사용한 라이브러리에 메모리릭에 문제가 있어, 이 과정중 개발 포럼에 Fix요청하는경우도 발생합니다.

메모리를 효율적으로 누수없이 잘사용하는것은 개발 초기 단계에서부터 실수하지 않도록 고려해야하는 부분입니다.


성능 개선을 위한, 로직 개선전략


커넥션을 미리준비

중계 API 는 중계 서비스로, DB SP에대한 어떠한 콜도 하지 않습니다. 여기에서 필요로하는 모든 데이터는 서비스 SERVER로부터 받게되며 서비스 SERVER 로 해당 기능 요청을 위임하게 됩니다. 이때 서비스 SERVER로의 커넥션이 필요하게 되며 RESTFUL 요청시마다 커넥션이 발생하면 커넥션 오버헤드가 발생하기때문에 SOCKET QUEUE 를 미리 준비하여 (20개) , 재사용하며 이때 발생하는 커넥션 타임을 0인 상태로 작동을 하게합니다.

모드분리

SOCKET QUEUE가 존재하지만, 모든 요청에대해 커넥션이 필요한것은 아니며,

커넥션이 필요없으면 필요없는 채로 작동되게 됩니다.

중계 API는 성능 향상및 커넥션수를 최적화하기 위해  다음과 같이 3가지 모드가 있습니다.
 

1.Share Mode

관련 api: GET SomeInfoList BY FILTER,GET SomeInfoList BY ID 공용으로 사용되는 정보 리스트의 경우,

리스트 동기화부터 모든 데이터를 모든 유져가 공용으로 사용할수 있으며, 이때 필요한 서버 커넥션 수는 유져수와 상관없이 ,

1개만 있으면 됩니다.

2.Login Mode

관련 api : SomeBUY, SomeEGISTRATION,Some UNREGISTRATION 로그인 모드일시,

AuthToken이 필수적으로 필요하며, 자기 자신에대한 처리가 필요함으로 서버 커넥션수는 유져수(AuthToken 발급수) 와 동일합니다. 

3.Guest Mode

관련 api: SomeInfomation,SomeSTATE,SomeLEADERBOARD 요청 타임시 갱신된 정보를 받아야할 필요가 있을시,

로그인 없이 준비된 커넥션 풀링 에의해 ( 커넥셕수 지정가능-현재 5개 ) 최신 정보를 획득할수 있게 됩니다.

4.기존 서버에 완료 신호 추가

기존 다운로드 클라이언트의 경우, 모든 기능에대해 실시간 통신을 하고 있으며 어떠한 요청에대해 비동기적으로 작동중입니다.

하지만 RestFul의 경우 해당 데이터에대한 처리 완료를 필수로 해야하며, 기존 서버의 처리 경우 데이터가 존재하지 않을시

아무런 반응이 없기때문에 중계 API는 어느시점까지 대기할지 알수가 없으며 지정된 타임아웃이 발생합니다.

이경우, 해당 데이터가 없으니 대기를 멈추어라는 신호를 기존 서버에 추가할수가 있습니다. (PK_SOMEAPI_STOP_SYNCWAIT)

타임아웃 발생률을 감소 시켜서 LOBBY API 성능향상이 약간 기대가 됩니다.


튜닝 가능 요소

성능에관련된 쓰레드수,스케쥴시간등은 옵션으로 뺴둬서 튜닝이 가능하게 하는것이 좋습니다.

초기에 이상적인 값을 예상할수는 있지만, 어떠한 값이 최고 효율을 내고 그값을 사용하기위한

정확한 계산식을 정확하게 집어내기가 어렵기 떄문입니다.


SETTING THREAD COUNT

RESTFUL ,  동시 호출가능한 쓰레드 수 조절(필요하면 API별로 쓰레드 수 조절가능, DB SP 튜닝시 SP별 정책달리하는것과 유사) - https://www.playframework.com/documentation/2.1.0/ThreadPools
parallelism-min = 300
parallelism-max = 300
 
WEBSOCKET THREAD 수 조절가능
                "org.atmosphere.cpr.broadcaster.shareableThreadPool=true",
                "org.atmosphere.cpr.broadcaster.maxAsyncWriteThreads=10",
                "org.atmosphere.cpr.broadcaster.maxProcessingThreads=10",
                "org.atmosphere.cpr.broadcaster.broadcasterLifeCyclePolicy=IDLE"
 

SETTING 내부 TCP커넥션수

내부 서비스 서버 연결 성능 향상하거나 병렬적으로 늘림
중계 API( 20THREAD )   ->  서비스 서버 ( 3 APP(1 PER 1THREAD)  ) - >  DB
LOGIN만 예를 들면, 톨게이트 진입전에는 20차선인데, 진입후 1차선이 되어버린다면  
이 경우 중계 API 를 아무리 성능 개선을 한다고 한들 서비스 SERVER에서 병목현상이 발생하게 됩니다.   
API용으로 만 사용되는 외부로 연결되지 않는 서비스 SERVER를 병렬적으로 늘리거나
서비스 SERVER의 성능을 개선이 필요한 부분입니다.  외와는 별개로 최적화값으로 작동을 시키기 위해
관련된 값들을 옵션화합니다.

SETTING TIMEOUT

TIMEOUT 시간 상황별로 세부 셋팅가능하게 조절
serviceTimeOut = 1000 * 10; //Api Time Out 10sec
packetShotTimeOut = 1500; //packet Time Out 1.5sec ,wait for other info
packetLongTimeOut = 5000; //packet Time Out 5sec ,wait for buyin,login
serviceTimeOut은 기본으로 10초이내에 어떠한 응답이라도 줘야하는것을 원칙으로 합니다.
packetLongTimeOut 의 경우 DB및 PostTransfer API와도 물려있기때문에 관련 패킷 요청시 5초이내에 응답을 줘야하는것을 원칙으로 합니다. (측정후 조정가능)
packetShotTimeOut 의 경우 빠르게 응답이 오기때문에 . 1.5초 이내에 응답을 받아야하는것을 원칙으로 합니다.

ServerCache

특정 시간(캐시타임이라고정의)내에 동일한 요청/동일한 반환값에 대해서는 서버가 다시 일할 필요없이
Endpoint Level 에서 아직 휘발되지 않은 메모리 데이터를 빠르게 반환합니다.


Link: 캐시 테스트


개발 초기 고려 할수있는 사항

0.01초의 성능향상이 확실한 부분은, 성능을 위한 가이드라인을 준수합니다.


1.사용중 인 OBJECT 보다 상황에 적절한 효율적인 OBJECT사용

String 객체 ==> StringBuilder

최초 String객체 생성후 동일객체에서  중복으로 3번이상 유틸함수(Replace,Trim,Left등등등)를 통해 재할당하는 경우가 아니면, 그냥 변환 필요없을것으로 예상됨
 

Json 라이브러리가 Lib별로 성능차이가 남..

현재 사용중 : JavaScriptSerializer <== 애가 젤 느림, 버리고 교체 필요

Multi,ActorSystem ==> EventBus

가급적 ActorSystem개수를  최소화함 ( 어플리케이션당 1개가 적당)
 

DownCasting은 가급적 피하며(object 사용시 빈번하게 발생함), 불가피할시 as 키워드 사용

현재 사용중인 라이브러리(로그,소켓,기타) 등등 특정버젼에 성능이슈가 발생하는지 혹은 최신버젼에
성능향상을 획기적으로 개선하였는지 여부를 항상 주시합니다.


프로파일 툴

부하 테스트시, 문제를 분석하고 찾을수 있는 프로파일 툴의 사용법을 익힙니다.. 불가피 하게 실서비스에서만 발생하는 문제를 찾기위한 Remote로 확인하는 방법까지 사용법을 익혀둡니다.

JAVAJCONSOLE,JPROFILE,https://visualvm.java.net/
C#Visual Studio Profile,CLR PROFILE,JETBRAIN .NET MEMORY / DOT TRACE
C++https://msdn.microsoft.com/en-us/library/x98tx3cf%28v=VS.100%29.aspx?f=255&MSPPError=-2147217396


LoadTestTool(Loadrunner)/측정툴(OpenNMS)

대규모 테스트를위해서는, 클라이언트 수행자를 다수개의 Node로 분산처리 할필요가 있습니다. 

직접 노드 테스트는 개발단계에서만 작성하고,실제로는 도메인에 물려서 L4 스위칭(BigIP)에의해 분산이 되는지? 그리고 분산된 사용자가, 다시 자신이 한번 사용한 노드를 재사용하는지?(퍼시던트 룰) 를 확인하는것은 성능뿐만 아니라, 웹소켓 오류를 줄일수 있는 주요 체크사항입니다. 노드 변경의 가능성은 항상있기때문에 Redis와 같은 메모리 DB를 사용하여 소프트웨어 적으로 보정해야할 필요도 있습니다. 이러한 테스트를 지원하려면, 물리컴퓨터 한대로는 가상시나리오를 실제와 유사하게 진행하기 어렵습니다. 물리 네트워크를 여러개 두는것은 비용적인 측면이있어서,가상 사용자수 테스트시 N개의 테스트노드 * 가상네트워크로 구성을 할수 있는 테스트툴 활용도가 필수입니다. 최고 접속 사용자가 낼수있는 최고 트래픽의 *1.5 정도를 목표 타켓을 잡습니다.

활용 툴:

  • Loadrunner: 부하 시나리오작성및 다양한 언어를 통해 스크립트 작성이가능하며(유닛테스트를 꼼꼼하게 작성했다면 활용가능),가상네트워크 구축을 통해 분산테스팅 환경을 지원합니다.

  • OpenNMS(시스템모니터링툴): 부하테스트시 시스템의 부하측정은 중요한 부분입니다.

  • 프로파일러(dotTrace,dotMemory): 시스템 모니터링툴이 하지 못하는, 개발 프레임워크 내에서 조금더 디테일한 프로파일링(메모리,오브젝트량,쓰레드상태등)을 할수가 있습니다. 보통 10개의 분산 서비스라고 하면 프로파일러 자체가 성능에 영향을 끼치므로 노드 한개정도만 리모트로 걸어둡니다.

  • 커스텀 제작 테스트툴: 대부분의 부하테스트는 스크립트에서 작성가능하지만,상황에따라 커스텀한 테스트툴 작성

테스트 장비 설정및 전략

  • 테스트 사이트 설정시 서비스 장비와 동일한 개수가 이상적이나, 상황에 맞게 목표타켓을 맞춰줍니다.

  • 실 사용자 계정을 복사하여 테스트는 대부분의 사이트가 불가하며, 활성화 유져수에 해당하는 가상유져및 가상네트워크를 사용하는 전략을 세웁니다.

  • 결제와 같은 API는 테스트가 불가할수 있습니다. 그러면 결제 API와 유사한 반응속도와 작동을 하는 더미 API 개발이 필요할수 있습니다.

  • API를 통해 DB성능 테스트가 자연스레 포함되기때문에, 서비스 DB의 데이터량(테스트에 관여안하는 Log성 Data도 포함)과 똑같으면 이상적입니다. 대용량 테스트는 TestDB의 디스크풀을 나게할수 있음으로, DBA와 모니터링 협조합니다.

  • 성능테스트는 주로 프로젝트 마무리 기간에 수행이되며, 성능이슈로 코드수정시 기능검증된 QA를 다시해야되는 불편한 상황이 생길수 있습니다. 풀테스트를 다시진행하여 개발완료시간을 소모하는 일이 발생하지 않게, 변경 영향범위를 최소화하여 예측해야합니다.

로드량 계획

여기까지가 되면, 이제 로드테스트를 할수 있는 기반이된것입니다.

기존 사용자 유입량을 정확하게 로그기반으로 Type별로 측정하여, 작성중인 API의 예상 호출량을 예측하여야합니다.

아래표는 실제 해당 접속자수에 모든로그를 조사하여 API호출량을 예측한것입니다.

측정역시 오차가 있을수 있기때문에 +50% 더 버틸수 있게 테스트 시나리오를 작성합니다.


Lobby/Games API

Scenario 1: Display the Lobby

Concurrent players in the lobby at peak time:

Current

With +50% margin

  • Download Client: 8,600
  • Mobile: 1,400
  • Total: 10,000
  • Download Client: 12,900
  • Mobile: 3,100
  • Total: 15,000

Estimation for the repartition of players within the different tabs:

Tab

% estimation

Zone7%
Cash32%
Tournament (SnG + Scheduled)53%
Casino8%

Requests:

Action

Endpoint

Frequency

Estimation

With +50% margin

Refresh the sso tokenPUT /keepalive/{authToken}every 5 min

= 2,000 calls / min

= 34 TPS

50 TPS
Get the list of zone gamesGET /zone/games/{playMode}/{gameType}/{buyin}/{limit}/{seats}?authToken={authToken}every 50 sec

7% players on zone tab = 700 players

= 14 TPS 

21 TPS
Get the list of cash tablesGET /cash/tables/{playMode}/{gameType}/{buyin}/{limit}/{seats}?authToken={authToken}every 50 sec

32% players on cash tab = 3,200 players

= 64 TPS

96 TPS
Get the list of tournamentsGET/tournaments/games/{playMode}/{gameFormat}/{tournamentType}/{gameType}/{buyin}/{limit}/{seats}?authToken={authToken}1 time

990 logins / min

53 % of players going to a tournament tab

= 8.7 TPS

13 TPS

In addition, each player will have a WebSocket connection to the Lobby Websocket API.

Scenario 2: Play a Game

Concurrent players playing a game at peak time:

Current

With +50% margin

Unique: 6,000

Total: 10,000

Unique: 9,000

Total: 15,000

Estimation for the repartition of players within the different game types:

Tab

% estimation

Zone8%
Cash32%
Tournament (SnG + Scheduled)60%

Requests:

Action

Endpoint

Frequency

Current peak / estimation

With +50% margin

Get buyin info for a zone gameGET /zone/buyin/{gameId}/{authToken}1 time

30 / min

= 0.5 TPS

0.75 TPS
Buyin to a zone gamePOST /zone/buyin/{gameId}/{authToken}1 time

30 / min

= 0.5 TPS

0.75 TPS
Get buyin info for a tournamentGET /tournaments/{tournamentId}/register/{authToken}1 time

153 / min

= 2.6 TPS

4 TPS
Buyin to a tournamentPOST /tournaments/{tournamentId}/register/{authToken}1 time

153 / min

= 2.6 TPS

4 TPS
Launch a gamePOST /launchGame/{gameFormat}/{gameId}/{authToken}1 time

224 / min

= 3.7 TPS

5.5 TPS
Get tournament informationGET /tournaments/{tournamentId}/info1 time

153 / min

= 2.6 TPS

4 TPS
Get tournament stateGET /tournaments/{tournamentId}/stateevery 30 sec

60% players in tournaments = 6,000

= 200 TPS

300 TPS
Get tournament table listGET /tournaments/{tournamentId}/tablesevery 10 sec

60% players in tournaments = 6,000

= 600 TPS

900 TPS
Get tournament leaderboardGET /tournaments/{tournamentId}/leaderboardevery 10 sec

60% players in tournaments = 6,000

= 600 TPS

900 TPS
Get my rank

GET /tournaments/{tournamentId}/rank/{authToken}

1 time

153 / min

= 2.6 TPS

4 TPS

In addition, each player will have a WebSocket connection to the Games API.

Other actions

Requests:

Action

Endpoint

Frequency

Current peak

With +50% margin

Unregister from a tournamentPOST /tournaments/{tournamentId}/unregister/{authToken}1 time

10 / min

= 0.16 TPS

0.25 TPS

Account API

Action

Endpoint

Frequency

Current peak / estimation

With +50% margin

Get account informationGET /account/{authToken}

1 time (launch DL)

Mobile: 87/min

DL: 224/min

= 5 TPS

7.5 TPS
Request play moneyPUT /account/playMoney/{authToken}1 time

Mobile: 8/min

DL: 52/min

= 1 TPS

1.5 TPS
Get preferencesGET /preferences/{preferenceType}/{authToken}1 time (launch DL)

1,632/min

= 27 TPS

41 TPS
Set preferencesPUT /preferences/{preferenceType}/{authToken}1 time

389/min

= 6.5 TPS

9.75 TPS
Get system wide messageGET/message/{messageType}/{messageId}/{clientType}/{authToken}1 time

10,000/min

= 166 TPS

250 TPS
Set fingerprintPUT /fingerprint/{authToken}1 time (launch DL)

Mobile: 87/min

DL: 990/min

= 18 TPS

27 TPS
Check profile statusPUT /checkProfile/{authToken}1 time (launch DL)

DL: 990/min

= 16.5 TPS

24.75 TPS
Get banned wordsGET /bannedWords1 time (launch DL)

Mobile: 58/min

DL: 1,100/min

= 19 TPS

29 TPS
Get server settingsGET /settings1 time (launch DL)

Mobile: 45/min

DL: 1,100/min

= 19 TPS

29 TPS
Get operator profileGET /operatorProfile1 time (launch DL)

DL: 1,100/min

= 18 TPS

27.5 TPS
Get tournament custom typeGET /tournamentCustomType1 time (launch DL)

DL: 8,600/min

= 143 TPS

215 TPS
Get banners infoGET /banners1 time (launch DL)

DL: 4,090/min

= 68 TPS

102 TPS
Get bonus feature statusGET /bonus/feature1 time

Mobile: 116/min

= 2 TPS

3 TPS
Get bonus indicatorGET /bonus/indicator/{authToken}every 5 min in DL client

DL: 2,000/min

= 33 TPS

50 TPS
Get active bonus informationGET /bonus/active/{authToken}1 time

Mobile: 80/min

= 1.5 TPS

2 TPS
Get available bonus listGET /bonus/available/{authToken}1 time

Mobile: 11/min

= 0.2 TPS

0.3 TPS
Get bonus detailsGET /bonus/{bonusId}/details/{authToken}1 time

Mobile: 9/min

= 0.15 TPS

0.23 TPS
Activate bonusPUT /bonus/{bonusId}/{action}/{authToken}1 time

Mobile: 1/min

= 0.01 TPS

0.02 TPS
Reorder bonusPUT /bonus/{bonusId}/move/{direction}/{authToken}1 time

Mobile: 1/min

= 0.01 TPS

0.02 TPS
Get HH listGET /hh/{gameFormat}/{authToken}1 time

Mobile: 9/min

DL: 154/min

= 2.7 TPS

4 TPS
Get HH detailsGET /hh/{gameFormat}/{handId}/{authToken}1 time

Mobile: 32/min

DL: 154/min

= 3 TPS

5 TPS
Send error log to serverPOST /logging/client/{logType}/{authToken}1 time

DL: 1,882/min

= 31 TPS

47 TPS

Calculation API

Action

Endpoint

Frequency

Current peak / estimation

With +50% margin

Get all-in percentages

POST /all-in-percentages

80% holdem

20% omaha


33% 5 board cards

33% 4 board cards

23% 3 board cards

11% no board cards


1000/min

= 16.5 TPS

25 TPS




가상시나리오 Time라인/EndPoint별 성공률
사용자 시나리오가 반영된 Endpoint/시간별 성공그래프
네트워크량 변화 측정
API 지속력 측정


사용자 유입시나리오를 반영한 로드테스트

9000 accounts + websocket

9000 account + websocket
Transaction Name 

Total

Count 

Failed

Count 

Error

ratio 

TPS

Total

Count 

Failed

Count

Error

ratio

TPS

Total

Count 

Failed

Count

Error

ratio

TPS
 Target

MaxLoad

OverallTarget MaxLoadOverallTarget MaxLoadOverall

Avg

Avg
AvgAvg
AvgAvg
Login9000




9000










KeepAlive2918720487.0250
18.847299991000.3350
20.763





getTournamentsByFilter163202281.4013
9.24821890740.3413
12.538





getTournamentsInfo490570.144
2.8156790004
3.902





getTournamentsState37003273311.98300
208.449511021419598.21300
269.576





getTournamentsTables1111262210641.90900
626.551153176066690.44900
876.489





getTournamentsLeaderboard10882369360.09900
624.885152504300900
876.461





getTournamentsRank4850120.254
2.8806740004
3.874





getTournamentsBuyinInfo48970
4
2.8146790004
3.902





tournamentsBuyin48960
4
2.8146788004
3.901





unRegister2330
0.25
0.14433620.600.25
0.214





Overall error rate











Image file


Max Clients9,000900015,000
Ramp up15min15min15min
Time to run10min10min70min
Ramp down5min5min5min
Remarks
  • Errors of Get Tournaments state were increased sharply when right after finishing ramp-up. 

다음날 서버상태.




로드테스트 깨알팁

부하테스트 스크립트를 최초에 작성시에는, 특정한 스텝을 잘못수행하여 API Error를 유발할수 있습니다. (돈이 없는데 결제를 계속 한다던지? / 중복로그인을 계속한던지? 등등 )

초기에 Error는 테스트 스크립트가 잘못된게 대부분이며 다음과 같은 초기 구축시 어려움이 있습니다.

  • 테스트 스크립트 안정화에 많은 시간을 소요하게됩니다.

  • 구축완료후 검증단계에서 실서비스 부하문제를 찾지못함(시나리오 실패)

  • 관련 개발 리소스가 생각보다 많이 투입됨

  • QA프로세스가 안잡힌 상태에서 먼저 수행하기 어려움이 있음

  • 모니터링툴이 구축이 선행됨

로드테스트는 개발보다 더많은 개발공정및 QA공정이 들어갈수 있으며 생각보다 고되고 당장 효과가 나는 만만한 작업이 아닙니다.

모든 기능을 단위로 테스트할수 있는 API여부,모든 유입을 측정할수 있는 로그시스템, 시스템자원을 디테일하게 카운팅할수 있는 모니터링시스템, 배포자동화 , QA안정화 이중 하나라도 안되어 있으면

대용량 로드테스트 시도는 고려할 필요가 있습니다. 기반 시스템이 제대로 안갖추어진 상태에서 진행은 아무것도 얻지 못할수도 있습니다.   

하지만 이러한 시도는 사이트 안정화에 도움이되는것을 기대할수 있으며, 그 과정에서 운영안정화로 바로 이어지지 않더라도,

내부적인 숨겨진 여러 성능 문제에 대해 발견을하고 해결방안이 없지만, 뭐가 문제인지 고민할수 있는 단계까지만 가도 성공적인 도입이지 않을까? 생각해봅니다.