서버 성능 테스트 Tips – if(kakao)dev 2019

  • 카카오 게임즈 발표
  • 성능 테스트의 목적
    • 시스템의 성능 기준을 정의
      – 예를 들어 ‘8만 tps에서 동접 1만이 수용 가능하고 그 상태에서 서버 cpu가 70%까지 사용 해야 한다’ 라는 기준
    • 실제 사용자의 액션과 유사한 시나리오를 작성하여 실제 유저의 부하와 유사한 부하 유입
      – 1. 이용자의 다양한 액션 누락 참조
    • 시스템의 병목 구간을 찾아서 튜닝 완료
      – 2. 최종 시스템의 성능테스트 누락
    • 임계값을 기반으로 이용자 증가/감소에 대응할 수 있는 데이터 제공
    • 시스템의 무결성을 검증하여 예상하지 못한 장애를 예방 및 대응
      – 3. 예외 상황에 대한 테스트 누락 참조
  • 성능 테스트 진행하면서 겪었던 시행착오 및 사례
    • 알고 있었지만 실수로 겪었던 사례
      1. 이용자의 다양한 액션 누락
        • 발생 문제 : 예상 동접은 한참 남았는데 PVP 서버 CPU 95%까지 증가.
        • 원인 : 이용자들은 실제로 한가지씩 차례대로가 아닌 다양한 형태로 컨텐츠를 소비한다.
          ( 그렇기 때문에 하나의 테스트 시나리오로는 실제 유저와 유사한 부하를 발생시킬수 없다.)
        • 해결 : 성능 테스트 방식을 변경.
          ( 기존 테스트 방식 : 하나의 성능 테스트 시나리오를 작성한 후 API를 순서대로 나열하고 한번씩 호출하며 반복적으로 부하를 주는 식으로 테스트 진행.
          변경한 테스트 방식 : 우선 다양한 이용자의 흐름에 맞춘 테스트 시나리오를 작성할 수 있도록 인원을 확보하여 각각 다른 스타일로 게임을 진행하게 한다. 그렇게 진행했던 내용들을 시나리오 문서로 작성하도록 한 후, 그 문서를 기준으로 실제 유저들이 동일한 플레이 시간이어도 API별로 호출 횟수가 다양하게 나온다는걸 확인할 수 있었고 그것을 기반으로 실제 유저가 이용 가능한 유사한 시나리오들을 작성할 수 있었고 그 시나리오들을 바탕으로 성능테스트를 진행하면서 기존 보다는 좀 더 실제 유저와 가까운 부하를 발생 가능하게 됨.)
      2. 최종 시스템의 성능 테스트 누락
        • 발생 문제 :
          – 서비스 런칭 후 동시 접속자 3만에서 장애
          – Memcached 응답속도가 느려지는 현상 발생
          – PHP-fpm의 프로세스에서 지연 현상 발견
          – 이후 응답 없음 현상이 발생함
        • 원인 : 분산 Memcached 구조에서의 친구 리스트 호출 횟수가 비정상적으로 증가하면서 php-fpm이 설치된 장비의 memory full.
        • 해결 :
          – Memcached 스케일 업(scale-up) 진행
          – Memcached에 있는 데이터들을 분할하는 작업 진행
          – 비정상적으로 호출되었던 친구 리스트 알고리즘 수정
        • 왜 성능 테스트 때는 발견되지 않았나?
          • 성능테스트를 진행 할 때 다수의 서버에서 하나의 memcached로 구성되어 테스트를 진행했으며 정상적인 결과를 확인했으나 성능 테스트 진행 후 런칭 전까지의 2~3주의 시간동안 5대의 분산구조로 memcached 구조 변경됨. 사실상 런칭스펙에 대해서는 성능테스트가 전혀 진행이 되지 않고 오픈이 된 것.
          • 이 문제는 기술적 부분보다는 성능 테스트 프로세스의 개선이 필요하다 판단됨
        • 개선 : 성능 테스트 프로세스 개선
          • 시스템에 대한 확장성 및 변경 가능성에 대해 리뷰 강화
          • 성능 테스트 진행 전 테스트 환경에 대해 담당 개발자 검증 단계 도입
          • 최종 런칭 스펙과 동일한 구성으로 성능 테스트 진행하도록 프로세스 강화
          • 성능 테스트 종료 후 시스템의 구조 변경은 서비스 담당자들이 합의하도록 변경
      3. 예외 상황에 대한 테스트 누락
        • 발생 문제 : 유저의 이탈 경로와 상세 분석을 위해 로그를 상세하게 넣었고 이 부분에 대해 우려를 했으나 성능 테스트 진행시에 별 이상은 없었음. 하지만 런칭 이후 로그 서버 CPU 95%, memory 100%로 로그 서버 다운되고 API서버도 이어서 다운. 이후에 다운된 서버를 올리는 작업을 진행하였으나 순차적으로 계속 다운.
        • 문제 해결을 위해 계속 서버(API서버와 로그서버는 연관관계가 없다고 생각하여 문제 발생 원인을 다른 곳에서 찾느라 해결이 더 늦어졌음)
        • 원인 : API 서버에서 로그를 썼는데 로그 서버가 죽어있어서 응답 대기 상태로 빠지는 현상 발생.
        • 개선 : 모든 구성 서버들에 대해 예외 상황 테스트 진행
          – 구성 서버들을 나열후에 각 서버들을 강제로 발생 시켜서 장애가 발생한 서버에서 어떠한 장애가 발생하는지 그 현상 파악 그리고 해당 서버에서 장애가 발생한 이후 다른 서버에서는 어떠한 현상들이 발생이 되는지, 정상적으로 동작을 하는지에 대한 내용 파악.
          – Auto scaling 및 DB Failover에 대해서도 사전에 검증 진행
  • 더 좋은 성능 테스트를 위해 준비하고 있는 것.
    • 수집된 데이터들을 바탕으로 시스템 구성 별 성능 예측
      : 성능 테스트를 하며 얻어지는 결과 데이터들을 분류 및 저장하고 있으며 시스템의 기본 성능에 대한 기준을 만들고자 하고 있다. 계속해서 쌓이고 있는 이 데이터들이 나중에는 빅데이터처럼 성능테스트 진행전 시스템 구성이나 서버 인스턴스만 보고도 대략적인 기준을 제시할 수 있지 않을까.
      – [ex]
      – AWS의 EC2 instance 종류 별 적정 TPS, 혹은 수용 가능한 동시 접속자 수
      예시) C4.xlarge(aws 서버타입)는 1000TPS 혹은 C4.xlarge 한대 기준 동시 접속자 3000명
      – 시스템 구성에 대한 적정 TPS
      예시) Front와 API로 구성되어 있고, PHP-FPM과 Nginx이니까 2000TPS
    • 현재의 성능 테스트를 개선하여 더 나은 성능 테스트 체계 구축
      : 현재 성능 테스트 툴로는 웹서버를 기준으로 하고 있고, 웹서버를 대상으로 응답을 주고받거나 http통신 그리고 웹소켓 으로 한정되어 있는 것이 현실.
      이에 그동안 (ngrinder를 사용하여)성능 테스트를 진행하며 원하는 데이터 확인을 위해 커스터마이징 했던 스크립트들을 오픈소스로 공개해서 많은 개발자들과 내용 공유를 하고 Library 개선하고 및 사례들을 수집 하기 위해 준비 중.
  • 사용하고 있는 성능 테스트툴
    • nGrinder
    • QnA 1 :
      성능테스트 툴로 ngrinder를 사용하는 이유 : 스크립트를 커스터마이징면서 사용이 가능하며 원하는 결과 값을 확인 가능하기 때문.
    • QnA 2 :
      nGrinder를 통해 모든 것을 모니터링 하는 것은 불가능 할텐데 그 외의 모니터링은 어떻게 진행하고 있는지 : 대상 서버에 대해서는 nGrinder를 사용해서 모니터링 하는 것이 불가능하기 때문에 이 부분은 개발사에서 제공하는 Zabbix 툴을 사용하여 모니터링하고 있음.

Junit을-이용한-단위테스트

JUnit

JUnit은 자바용 단위 테스트 작성을 위한 산업 표준 프레임워크다.

JUnit assert 주요 메서드 및 사용예시

assert 메서드설명
assertArrayEquals(a, b);배열 A와 B가 일치함을 확인한다.
assertEquals(a, b);객체 A와 B가 일치함을 확인한다.
assertSame(a, b);객체 A와 B가 같은 객임을 확인한다. assertEquals 메서드는 두 객체의 값이 같은가를 검사는데 반해 assertSame메서드는 두 객체가 동일한가 즉 하나의 객인 가를 확인한다.(== 연산자)
assertTrue(a);조건 A가 참인가를 확인한다.
assertNotNull(a);객체 A가 null이 아님을 확인한다.

JUnit Annotation 사용 예시

  • 스프링 프레임워크 기반의 JUnit 테스트를 위한 세팅
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={“file:WebContent/WEB-INF/classes/applicationContext*.xml”})

Spring 기반의 테스트 코드 작성을 위해 테스트 클래스 상단에 @RunWith(SpringJUnit4ClassRunner.class) 구문을 추가한다.
Spring 프레임워크 context 파일을 테스트 수행시에도 동일하게 로딩하기 위해 @ContextConfiguration(locations={“file:WebContent/WEB-INF/classes/applicationContext*.xml”}) 과 같은 형태로 프로젝트의 스프링 설정파일을 설정해 준다.

  • 메서드 수행시간 제한하기
@Test(timeout=5000)

단위는 밀리초이며 이 메서드가 결과를 반환하는데 5,000밀리초가 넘긴다면 테스트는 실패한다.

  • Exception 테스트
@Test(expected=RuntimeException.class)

해당 클래스는 RuntimeException이 발생해야 한다. 만약 테스트에서 RuntimeException이 발생하지 않을 경우 실패한다.

  • 테스트 건너뛰기
@Test(timeout=5000)
@Ignore(value=”여기는 테스트 안할거야”)

@Ignore 어노테이션을 추가하면 해당 메서드는 테스트를 건너뛰게 되며 JUnit4는 성공 및 실패 개수와 함께 건너뛴 테스트 수도 포한된 결과 통계를 제공한다.

정렬(Sort)

단순 리스트를 정렬할때 보통 Collections.sort()를 사용한다. // 오름차순

ex)
ArrayList list = new ArrayList();

list.add("d");
list.add("c");
list.add("b");
list.add("a");

Collections.sort(list); 

출력 : abcd

하지만 개발을 하다보니 VO를 List에 담는 경우도 있는데~
이때 List를 Collections.sort()에 담아서 실행하게 된다면 어떤 기준으로 정렬할지 모르기 때문에 에러가 발생한다.

List list = memberService.getUserList();
Collections.sort(list); //에러 발생

이때 단순 정렬이라면 Comparable을 사용해도 되고, 다른 여러가지 조건을 더 주려면 Comparator을 사용하면 된다.
객체 정렬 Comparator : 기본 정렬 기준 외에 다른 여러 기준으로 정렬하고자 할 때 사용하는 클래스
[형태]
Collections.sort(객체, new Comparator<타입>(){
});

선언하고 compare 메소드를 구현하면 된다.

ex)
     Collections.sort(list, (o1, o2) → {
        if (o1.getNumber() > o2.getNumber()) return 1;
        else if (o1.getNumber() < o2.getNumber()) return -1;
        else {return 0;}
     });


참고 :
https://cchoimin.tistory.com/entry/%EA%B0%9D%EC%B2%B4-%EC%A0%95%EB%A0%AC-Comparator?category=827467