시뮬레이션(Simulation) 정의하기
시뮬레이션(Simulation) 정의하기
Gatling 테스트는 Simulation이라는 부모 클래스를 상속해 만드는 하나의 실행 단위예요. 모든 시나리오와 인젝션 프로필, 어서션을 이 안에 끼워 넣고 실행해요. 이 글에서는 시뮬레이션의 주요 구성 요소인 SDK 임포트, 시나리오 정의, 시뮬레이션 정의, 훅(hooks)을 차례로 살펴볼게요.
Simulation은 Gatling이 테스트를 실행할 수 있도록 테스트가 반드시 상속해야 하는 부모 클래스(JavaScript·TypeScript에서는 함수)예요.
본문
SDK 임포트
// Gatling 핵심 구조 DSL에 필요
import io.gatling.core.Predef._
// Gatling HTTP DSL에 필요
import io.gatling.http.Predef._
// jdbcFeeder를 쓰지 않으면 생략 가능
import io.gatling.jdbc.Predef._
// 단위 있는 시간 지정("5 minutes" 등)에 사용
import scala.concurrent.duration._
setUp — 필수 등록
테스트의 대부분 조각(시나리오, 프로토콜, 헤더, 인젝션 프로필 등)은 별도 헬퍼 클래스로 추출해 자기만의 테스트 라이브러리를 만들 수도 있어요. 그러나 시뮬레이션에서 반드시 지켜야 할 규칙이 하나 있어요 — 생성자에서 setUp 메서드를 정확히 한 번 호출해 테스트 컴포넌트를 등록해야 해요.
val scn = scenario("scn")
// 등등...
setUp(scn.inject(atOnceUsers(1)))
프로토콜 설정
HttpProtocol을 전역으로, 또는 인구(population)마다 다르게 설정할 수 있어요.
// HttpProtocol을 전역으로 설정
setUp(
scn1.inject(atOnceUsers(1)),
scn2.inject(atOnceUsers(1))
).protocols(httpProtocol)
// 인구마다 다른 HttpProtocol 설정
setUp(
scn1.inject(atOnceUsers(1)).protocols(httpProtocol1),
scn2.inject(atOnceUsers(1)).protocols(httpProtocol2)
)
수용 기준(어서션)
어서션은 setUp에 설정해요. 테스트 통과 여부를 정하는 기준이에요.
setUp(scn.inject(atOnceUsers(1)))
.assertions(global.failedRequests.count.is(0))
전역 pause 설정
setUp에서 pause 동작을 전역으로 지정할 수 있어요.
setUp(scn.inject(atOnceUsers(1)))
// 시뮬레이션의 pause를 비활성화
.disablePauses
// 각 pause의 길이는 pause(duration) 요소에 지정된 대로
.constantPauses
// 평균이 pause(duration) 값인 균등분포로 pause
.uniformPauses(0.5)
.uniformPauses(2.seconds)
// 평균이 pause(duration) 값, 표준편차가 지정한 시간인 정규분포로 pause
.normalPausesWithStdDevDuration(2.seconds)
// 평균이 pause(duration) 값, 표준편차가 평균의 일정 비율인 정규분포로 pause
.normalPausesWithPercentageDuration(20.0)
// 평균이 pause(duration) 값인 지수분포로 pause
.exponentialPauses
// pause 길이를 함수로 계산 (밀리초 기준, 채워진 값은 무시)
.customPauses(session => 5L)
인구마다 다른 pause 설정도 가능해요.
setUp(
scn1.inject(atOnceUsers(1)).disablePauses,
scn2.inject(atOnceUsers(1)).exponentialPauses
)
처리량(Throughput) 셰이핑
가상 사용자 수 대신 초당 요청 수(처리량) 기준으로 생각하고 싶을 때도 있어요. 가상 사용자가 각각 요청을 하나만 수행한다면 open 모델(예: constantUsersPerSec)을 쓰는 게 좋아요. 그게 아니라면 어떤 요청이 실행될지 제어할 수단이 없어서 문제가 생기기 쉬워요. 그래도 throttle 메서드를 시도해 볼 수 있어요. throttle은 전역 또는 시나리오별로 정의할 수 있는데, 하는 일은 두 가지예요.
- 모든 pause를 비활성화해요.
- **처리량을 상한(cap)**으로 잡아요. pause를 비활성화한 상태에서 시뮬레이션이 정상적으로 만들 수 있는 처리량보다 높은 처리량을 만들어 내지는 못 해요.
스로틀링은 현재 HTTP 요청과 JMS에만 지원돼요. 스로틀링은 단일 요청 시나리오에만 써야 해요 — 그렇지 않으면 여러 요청 사이의 분포가 불균형해질 수 있어요. 그리고 초과 트래픽이 무한 큐로 밀려들어 정상 처리량이 훨씬 높으면 OutOfMemoryError가 날 수 있으니 주의하세요.
setUp(
scn.inject(constantUsersPerSec(100).during(30.minutes))
).throttle(
reachRps(100).in(10.seconds),
holdFor(1.minute),
jumpToRps(50),
holdFor(2.hours)
)
이 시뮬레이션은 10초 램프로 100 req/s에 도달한 뒤 1분 유지하고, 50 req/s로 점프한 뒤 2시간 유지해요.
스로틀링의 빌딩 블록은 다음과 같아요.
reachRps(target).in(duration): 주어진 시간 동안 램프하며 목표 처리량을 향해 갑니다.jumpToRps(target): 주어진 목표 처리량으로 즉시 점프해요.holdFor(duration): 현재 처리량을 주어진 시간 동안 유지해요.
Kotlin에서 in은 예약어이므로 백틱(in)으로 보호하거나 during 별칭을 쓰세요.
최대 실행 시간(maxDuration)
마지막으로, maxDuration으로 실행 시간 제한을 걸 수 있어요. 일부 가상 사용자가 아직 실행 중이라도 이 제한에 도달하면 강제로 실행을 종료해요. 실행 시간을 예측할 수 없을 때 시뮬레이션의 길이를 제한할 때 유용해요.
setUp(
scn.inject(rampUsers(1000).during(20.minutes))
).maxDuration(10.minutes)
훅(Hooks)
Gatling은 두 가지 훅을 제공해요.
before: 시뮬레이션이 실제로 실행되기 전에 임의 코드를 실행해요.after: 시뮬레이션이 실제로 실행된 후에 임의 코드를 실행해요.
라이프사이클은 다음과 같아요.
- Gatling 시작
- 시뮬레이션 생성자 호출 —
before·after훅에 지연되지 않은 클래스 본문의 모든 코드 실행 before훅 실행- 시뮬레이션 실행
- 시뮬레이션 종료
after훅 실행- 활성화되어 있으면 HTML 보고서 생성
- Gatling 종료