You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

개요

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

에서 웹서비스 요구에따라 , 기존 실시간 서비스를  중계 웹서비스 방식으로 변경,

개발은 완료되어 테스트되고 QA SignOFF가 났지만

이미 운영중인 최대동접(2만) * 1.5 배 수준의 로드 테스트 검증 과정의 고뇌와 측정 자료를 기록한 자료


3만명 가상 동접의 Load테스트 TPS 목표와 실제 측정 자료


측정 히스토리

클라이트가 아무 액션을 하지 않는 서버의 상태의 스냅샷을 기입합니다. 클라이언트가 아무 액션을 하지 않는다고 서버가 놀고있지는 않습니다. 사용자가 없이 흘러간 시간별 스냅샵 비교는 중요한 테스트 부분입니다.

기본적으로 백그라운드에서 작동하는 서비스가 있기때문에 0 - 1 액션의 Diff는 변화량을 측정하기가 어렵거나 미비합니다. 최소 100Action이상 사용자가 늘어나는 상황에서 비교를 하는게 의미 있는 데이터이나, 1액션 자체에 성능 문제가 있을수도 있기때문에 사소한것이라도 검증을 하는게 권장됩니다.

테스트 스크립트도 완성이되고, 1Node 테스트가 어느정도수행이되면 클러스터로 여러대를 구성하여 추가 테스트를 수행하게됩니다. 1Node 테스트때 찾지 못한 여러가지 문제를 찾게됩니다. 스케일아웃을 했다고 성능이 늘어났을것으로 기대하지만 예상외로 1Node일때 문제없는 성능부분이 스케일 아웃이되면서 예측하지 못한 많은 성능 문제가 발생하게됩니다.

TestDOCNote
Object Trace Test at 1 Action측정자료1액션당 변화량 측정
Object Trace Test at NoAction측정자료AKKA/Atmosphere/Our Class Object 비활동 베이스 변화량 측정
Object Trace Test at NoAction in chatServer
아무 활동 안하는 Chat 서버의 메모리 변화량이 비정상으로 보임,일반적 GC 활동으로 잠정 결론
MemUsage측정자료Total/.net/JavaHeap/JavaPermgen/Unused+unknown(JNI)
Diff Dump측정자료test Lobby API in a simple scenario (send a few requests), make 3 memory dumps (before/during/after) and check Retained memory
Trace Thread
Lobby API 에서 Topic 관련 쓰레드가 , 비활동중임에도 불구하고 Running 상태가 반복됨 ( 정상적인 쓰레드 활동인지 체크해야함),해결반안 ActorSystem을 다중으로 사용하지말고, Event Bus로 전환 , AKKA의 오브젝트가 많아 진것도 이로인한 디펙인듯 보임
SSL vs None SSLSSL의 최초 커넥션타임이 길고,약간의 메모리증가 무시할수 있는수준의 약간의 메모리증가
JNI Call TestJNI 는 충분히 빠름 : Lock블락이 포함된 C# 코드를 jni를 통해 5000번 콜한게 20ms이내에 완료됨logger.debug( “PerformTestJNI ProcessedTime: ” + Long.toString(processTime) )


성능 개선을 위한 기타 정보

가비지 컬렉트가 없는 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개 ) 최신 정보를 획득할수 있게 됩니다.
 
 
기존 서버에 완료 신호 추가
기존 다운로드 클라이언트의 경우, 모든 기능에대해 실시간 통신을 하고 있으며 어떠한 요청에대해 비동기적으로 작동중입니다.
하지만 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사용
a.
String 객체 ==> StringBuilder
:최초 String객체 생성후 동일객체에서  중복으로 3번이상 유틸함수(Replace,Trim,Left등등등)를 통해 재할당하는 경우가 아니면, 그냥 변환 필요없을것으로 예상됨

 
b. Json 라이브러리가 Lib별로 성능차이가 남..
현재 사용중 : JavaScriptSerializer  <== 애가 젤 느림, 버리고 교체 필요

  
2.Multi,ActorSystem ==> EventBus
가급적 ActorSystem개수를  최소화함 ( 어플리케이션당 1개가 적당)
 
3.DownCasting은 가급적 피하며(object 사용시 빈번하게 발생함),  불가피할시  as 키워드 사용


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

string builder 테스트 Json 차이보기 캐스팅 성능이슈


프로파일 툴


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

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

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를 다시해야되는 불편한 상황이 생길수 있습니다. 풀테스트를 다시진행하여 개발완료시간을 소모하는 일이 발생하지 않게, 변경 영향범위를 최소화하여 예측해야합니다.

성능 테스트 결과물


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



-부하테스트 스크립트를 최초에 작성시에는, 특정한 스텝을 잘못수행하여 API Error를 유발할수 있습니다. (돈이 없는데 결제를 계속 한다던지? / 중복로그인을 계속한던지? 등등 ) 초기에 Error는 테스트 스크립트가 잘못된게 대부분이며 다음과 같은 초기 구축시 어려움이 있습니다.

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

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

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

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

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

여러가지 리스크가 있음에도 ,한번 구축이되면 재사용되어 운영 사이트 안정화에 도움이되는것을 기대할수 있으며, 그 과정에서 운영안정화로 바로 이어지지 않더라도, 내부적인 숨겨진 여러 성능 문제에 대해 발견을하고 해결방안을 고민할수 있는 단계까지만 가도 성공적인 도입이지 않을까? 생각해봅니다.

  • No labels