profile image

L o a d i n g . . .

728x90

여러 개발 방법론에서는 개발 중 테스트 코드를 작성하는 것을 권고합니다. 저는 그 방법론을 충실히 따라 기능 단위 테스트 코드를 열심히 작성하고 있습니다. 하지만 프로덕션 환경에 이 코드를 올렸을 때 정상적으로 동작할지 항상 걱정이 들었습니다 . "내가 생각한 것보다 더 많은 요청이 들어오면 애플리케이션은 어떻게 동작할까?" 등 다양한 부하 상황에서 애플리케이션이 어떻게 동작하는지 궁금했습니다. 이 궁금증을 해결하기 위해 Gatling을 활용했습니다. 

Gatling 이란 

Gatling은 오픈소스 부하 테스트 소프트웨어입니다. 개발 pipeline과 지속적으로 통합시킬 수 있는 소프트웨어이기 때문에 개발의 모든 단계에서 다양한 방법으로 테스트가 가능합니다. Gatling은 maven을 통해 실행시킬 수 있습니다 

Gatling은 다음 github 저장소에서 쉽게 다운로드가 가능합니다. 
- java: https://github.com/gatling/gatling-maven-plugin-demo-java
- kotlin: https://github.com/gatling/gatling-maven-plugin-demo-kotlin 
- scala: https://github.com/gatling/gatling-maven-plugin-demo-scala

테스트 

Open vs Closed System

테스트 이전에 중요한 open system과 closed system에 대해 살펴보겠습니다. 

Open System 

  • 단위 시간당 시스템에 유입되는 유저의 요청량을 조절 가능 
  • 단위 시간당 발생하는 총 요청량을 조절할 수 없음 
  • 애플리케이션이 요청을 처리할 수 없더라도 지속적으로 요청이 발생할 수 있음 
  • 대부분의 웹 사이트의 동작 방식 
  • Gatling의 injectOpen 메서드를 사용 
setUp(
  scn.injectOpen(
    nothingFor(4), // 4초간 정지 
    atOnceUsers(10), // 10명의 유저가 동시에 요청
    rampUsers(10).during(5), // 유저를 10명으로 점진적으로 5초간 증가 
    constantUsersPerSec(20).during(15), // 초당 20명의 유저가 15초 동안 요청 
    constantUsersPerSec(20).during(15).randomized(), // 랜덤한 간격으로 (초당 20명의 유저가 15초 동안 요청)하는 것처럼 처리 
    rampUsersPerSec(10).to(20).during(10), // (10초 duration)초당 10명의 유저에서 초당 20명의 유저가 요청하는 형태로 균일하게 증가하게 진행
    rampUsersPerSec(10).to(20).during(10).randomized(), // (10초 duration)초당 10명의 유저에서 초당 20명의 유저가 요청하는 형태로 비균등하게 증가하게 진행
    stressPeakUsers(1000).during(20) // 순간적으로(20초 기간동안) 1000명이 요청하도록 처리 
  ).protocols(httpProtocol)
);

Close Sytem 

  • 단위 시간당 발생하는 총 요청량을 조절할 수 있음 
  • 시스템을 사용 가능한 최대 유저 수가 정해진 경우 
  • Gatling의 경우 injectClosed 메서드를 사용
setUp(
  scn.injectClosed(
    constantConcurrentUsers(10).during(10), // 10초 동안 10명의 유저가 요청하는 형태
    rampConcurrentUsers(10).to(20).during(10) // 10초 동안 10명의 유저에서 20명의 유저로 증가하면서 요청하는 형태
  )
);

 

그럼 간단한 Spring Boot Application을 생성해서 테스트를 진행하겠습니다. 관련 코드는 하단의 저장소에서 확인할 수 있습니다.

https://github.com/seonwoo960000/spring-boot-gatling

 

GitHub - seonwoo960000/spring-boot-gatling: Gatling test repository integrated with Spring Boot

Gatling test repository integrated with Spring Boot - GitHub - seonwoo960000/spring-boot-gatling: Gatling test repository integrated with Spring Boot

github.com

우선 간단한 controller을 생성하도록 하겠습니다. 해당 API는 "hello"라는 String을 반환합니다. 요청을 처리하는데 1초 걸리는 것을 시뮬레이션하기 위해 Thread.sleep(1000)을 설정하겠습니다. "hello"를 콘솔에 출력해서 gatling에 의해 정상적으로 요청이 들어오는지 확인해보겠습니다.

test 1 API
Gatling closed model test

위의 코드는 "http://localhost:8080/hello" endpoint에 20초 동안 100명의 유저의 동시 요청이 발생했을 때 애플리케이션이 어떻게 동작하는지 확인하는 코드입니다. 해당 코드를 실행하기 위해서 다음 커맨드를 실행합니다. 

./mvnw gatling:test

해당 커맨드를 실행하면 target/gatling 디렉터리 하위에 결과 리포트가 생성됩니다.

결과 리포트
브라우저에서 결과 리포트 확인
결과 리포트

생성된 결과 리포트에서 주요 지표에 대한 정의는 다음과 같습니다. 

  • Active Users: (1초 전 active users 수) + (현 시간에 새로 요청을 시작한 유저의 수) - (1초 전 요청의 처리가 완료된 유저의 수)
  • percentile: 50th pct, 75 th pct.. 99th pct는 percentil을 의미한다. 99th pct가 1152ms이라면 99%의 요청이 모두 1152ms 이전에 처리가 완료됐음을 의미합니다.

다음으로는 open system에서 테스트를 진행해보겠습니다.  

Open System 테스트

이번 테스트에서는 query parameter을 요청에 포함시키고 response body도 정상적으로 반환되는지 확인해보겠습니다. 

test 2 API
Test 2 gatling

Open system test의 형태로 테스트를 진행해보겠습니다. 20초 동안 초당 100명의 유저의 요청이 유입되는 형태로 테스트를 진행했습니다.

Test 2 결과

모든 요청이 정상적으로 처리됐음을 알 수 있습니다. 로컬에서 실행되는 제 애플리케이션의 한계를 테스트해보고 싶어 다음과 같이 설정을 변경해보겠습니다. 

test 2의 설정 변경

총 120초 동안 초당 유입되는 유저의 요청량을 10에서 300으로 증가시키는 형태로 테스트를 진행했습니다. 결과는 다음과 같습니다.

test 2 결과

유저의 요청량이 점차 증가하면서 처리되지 못하는 유저의 요청이 대기 큐에 쌓이게 됩니다. 처리되지 못한 유저의 요청은 대기하게 되므로 점차 응답 시간도 증가하게 됩니다. 특정 시간 이후에는 더 이상 유저의 요청이 유입되지 않으면서 대기 중인 유저 요청들이 처리되고 active user의 수(노란색 선)가 감소하는 형태를 보입니다.

 

마무리 

제대로 된 테스트를 수행하기 위해서는 프로덕션 환경과 최대한 비슷한 머신에서 동작하는 애플리케이션을 대상으로 테스트해야 합니다. 제가 로컬에서 테스트한 방식은 컴퓨팅 리소스를 다양한 프로세스와 나눠서 사용하기 때문에 지표가 부정확할 확률이 높습니다.

Gatling을 활용해서 다양한 API와 다양한 시나리오를 테스트할 수 있습니다. 프로젝트 개발을 시작하는 동시에 gatling 환경도 함께 구성한다면 언제든 부하 테스트를 진행할 수 있다는 장점이 있을 것 같습니다. 또한 git을 통해 gatling을 관리하게 되면 부하 테스트 이력도 추적할 수 있다는 장점이 있습니다. 

728x90
복사했습니다!