nGrider 사용
https://github.com/naver/ngrinder/releases
Releases · naver/ngrinder
enterprise level performance testing solution. Contribute to naver/ngrinder development by creating an account on GitHub.
github.com
https://notspoon.tistory.com/48
nGrinder 성능테스트 사용법 및 테스트 예제
1. nGrinder nGrinder는 네이버에서 제공하는 서버 부하 테스트 오픈 소스 프로젝트이다. 애플리케이션을 개발하고 nGrinder에서 여러가지 가상 시나리오를 만들어 트래픽에 몰렸을 때 성능을 측정할
notspoon.tistory.com
https://github.com/naver/ngrinder/wiki/Getting-Started
Getting Started
enterprise level performance testing solution. Contribute to naver/ngrinder development by creating an account on GitHub.
github.com
Releases · naver/ngrinder
enterprise level performance testing solution. Contribute to naver/ngrinder development by creating an account on GitHub.
github.com
nGrinder의 동작 원리
성능 테스트
- 실사용 환경과 비슷한 환경에서 테스트를 진행하는 것
성능 테스트의 목적
- 서비스나 서비스 시스템의 문제점 개선
트랜잭션
- 하나의 요청과 그에 대한 응답의 묶음
TPS
- 1초에 처리할 수 있는 트랜잭션의 수
응답 시간
- 사용자가 요청을 보낸 시점부터 처리 결과가 화면에 보여질 때까지의 시간
부하 테스트
- 순간적으로 최대치의 트래픽 부하를 주는 것
부하테스트의 목적
- 리소스 병목 탐색, 버그 탐색
- 이벤트 상황과 같은 순간 트래픽 최대치, 한계치를 탐색
스트레스 테스트
- 장기적으로 부하를 주는 것
스트레스 테스트의 목적
- 장기간 부하 발생에 대한 한계치를 탐색, 예외 동작 상황 확인
- 데이터베이스 failover 상황, 자동 복구, 예외 동작 상황 확인
nGrinder
- 네이버에서 성능 측정 목적으로 grinder 기반으로 개발한 오픈소스 프로젝트
- jython과 groovy 두 타입으로 스크립트 작성 가능
Agent
- 성능 측정에 사용할 Agent
- Agent를 여러개로 구성하고 싶은 경우 Docker 나 cloud service 를 고려해 볼 수 있다.
Vuser per agent
- Agent 당 설정할 가상 사용자 수
- 동시에 요청을 날리는 사용자
Process / Thread
- 하나의 Agent에서 생성할 프로세스와 스레드 개수
Script
- 성능 측정 시 각 Agent 에서 실행할 스크립트
- 우리가 생성한 Script를 추가한다.
Duration
- 성능 측정 수행 시간
Run Count
- 스레드 당 테스트 코드를 수행하는 횟수
- Run Count와 Duration의 경우 둘 중 하나만 선택해서 기간 동안 실행하거나, Run Count 만큼 실행하게 한다.
========================================================================================
Enable Ramp-up
- 성능 측정 과정에서 가상 사용자를 점진적으로 늘리도록 활성화
Initial Count
- 처음 시작 시 가상 사용자 수
Initial Sleep Time
- 테스트 시작 시간
Incremental Step
- Process 또는 thread 를 증가시키는 개수
Interval
- Process 또는 Thread를 증가시키는 시간 간격
java '-javaagent:C:\STS\ngrinder-agent\lib\grinder-dcr-agent-3.9.1.jar' '-Djna.library.path=C:\Users\Haru\.ngrinder_agent\file-store\admin\current\lib' '-Dngrinder.connection.reset.on.each.test.run=true' '-Dpython.path=C:\Users\Haru\.ngrinder_agent\file-store\admin\current\lib' '-Dpython.cachedir=C:\Users\Haru\AppData\Local\Temp\jython' '-Dngrinder.etc.hosts=DESKTOP-OJLTR54:127.0.0.1,localhost:127.0.0.1,' '-Dngrinder.enable.local-dns=true' '-Duser.dir=C:\Users\Haru\.ngrinder_agent\file-store\admin\current' '-Dngrinder.context=agent' '-Dhttps.protocols=TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv3' '-Djsse.enableSNIExtension=false' '--add-opens' 'java.base/java.net=ALL-UNNAMED' '-Xms1024m' '-Xmx1024m' -classpath 'C:\STS\ngrinder-agent\lib\ngrinder-runtime-3.5.9.jar;;.;C:\STS\ngrinder-agent\lib\ngrinder-runime-3.5.9.jar;C:\STS\ngrinder-agent\lib\asm-9.0.jar;C:\STS\ngrinder-agent\lib\commons-io-2.6.jar;C:\STS\ngrinder-agent\lib\commons-lang-2.6.jar;C:\STS\ngrinder-agent\lib\dnsjava-3.2.2.jar;C:\STS\ngrinder-agent\lib\grinder-core-3.9.1.jar;C:\STS\ngrinder-agent\lib\grinder-dcr-agent-3.9.1.jar;C:\STS\ngrinder-agent\lib\grinder-http-3.9.1.jar;C:\STS\ngrinder-agent\lib\grinder-httpclient-3.9.1.jar;C:\STS\ngrinder-agent\lib\groovy-3.0.5.jar;C:\STS\ngrinder-agent\lib\groovy-datetime-3.0.5.jar;C:\STS\ngrinder-agent\lib\groovy-json-3.0.5.jar;C:\STS\ngrinder-agent\lib\groovy-sql-3.0.5.jar;C:\STS\ngrinder-agent\lib\groovy-templates-3.0.5.jar;C:\STS\ngrinder-agent\lib\groovy-xml-3.0.5.jar;C:\STS\ngrinder-agent\lib\hamcrest-all-1.1.jar;C:\STS\ngrinder-agent\lib\hamcrest-core-2.2.jar;C:\STS\ngrinder-agent\lib\httpcore5-5.0.3.jar;C:\STS\ngrinder-agent\lib\httpcore5-h2-5.0.3.jar;C:\STS\ngrinder-agent\lib\jna-5.14.0.jar;C:\STS\ngrinder-agent\lib\jna-jpms-5.14.0.jar;C:\STS\ngrinder-agent\lib\jna-platform-jpms-5.14.0.jar;C:\STS\ngrinder-agent\lib\json-20090211.jar;C:\STS\ngrinder-agent\lib\junit-4.13.1.jar;C:\STS\ngrinder-agent\lib\logback-classic-1.2.3.jar;C:\STS\ngrinder-agent\lib\logback-core-1.2.3.jar;C:\STS\ngrinder-agent\lib\ngrinder-groovy-3.5.9.jar;C:\STS\ngrinder-agent\lib\ngrinder-runtime-3.5.9.jar;C:\STS\ngrinder-agent\lib\picocontainer-2.13.6.jar;C:\STS\ngrinder-agent\lib\slf4j-api-1.7.30.jar' net.grinder.engine.process.WorkerProcessEntryPoint
2024-04-22 09:29:46,379 INFO Setting of nGrinder local DNS successfully
2024-04-22 09:29:46,382 INFO The Grinder version 3.9.1
2024-04-22 09:29:46,383 INFO Java(TM) SE Runtime Environment 17.0.10+11-LTS-240: Java HotSpot(TM) 64-Bit Server VM (17.0.10+11-LTS-240, mixed mode, sharing) on Windows 11 amd64 10.0
2024-04-22 09:29:46,394 INFO time zone is KST (+0900)
2024-04-22 09:29:46,441 INFO worker process 0 of agent number 0
2024-04-22 09:29:46,451 INFO Instrumentation agents: byte code transforming instrumenter for Java
2024-04-22 09:29:46,971 ERROR Script error - Error while initialize test runner
net.grinder.engine.common.EngineException: Error while initialize test runner
at net.grinder.scriptengine.groovy.GroovyScriptEngine.<init>(GroovyScriptEngine.java:71)
at net.grinder.scriptengine.groovy.GroovyScriptEngineService.createScriptEngine(GroovyScriptEngineService.java:87)
at net.grinder.engine.process.ScriptEngineContainer.getScriptEngine(ScriptEngineContainer.java:105)
at net.grinder.engine.process.GrinderProcess.run(GrinderProcess.java:345)
at net.grinder.engine.process.WorkerProcessEntryPoint.run(WorkerProcessEntryPoint.java:87)
at net.grinder.engine.process.WorkerProcessEntryPoint.main(WorkerProcessEntryPoint.java:60)
Caused by: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during conversion: Unsupported class file major version 61
java.lang.IllegalArgumentException: Unsupported class file major version 61
at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:196)
at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:177)
at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:163)
at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:284)
at org.codehaus.groovy.ast.decompiled.AsmDecompiler.parseClass(AsmDecompiler.java:81)
at org.codehaus.groovy.control.ClassNodeResolver.findDecompiled(ClassNodeResolver.java:251)
at org.codehaus.groovy.control.ClassNodeResolver.tryAsLoaderClassOrScript(ClassNodeResolver.java:189)
at org.codehaus.groovy.control.ClassNodeResolver.findClassNode(ClassNodeResolver.java:169)
at org.codehaus.groovy.control.ClassNodeResolver.resolveName(ClassNodeResolver.java:125)
at org.codehaus.groovy.ast.decompiled.AsmReferenceResolver.resolveClassNullable(AsmReferenceResolver.java:57)
at org.codehaus.groovy.ast.decompiled.Annotations.createAnnotationNode(Annotations.java:40)
at org.codehaus.groovy.ast.decompiled.DecompiledClassNode.addAnnotations(DecompiledClassNode.java:257)
at org.codehaus.groovy.ast.decompiled.DecompiledClassNode.lambda$createMethodNode$1(DecompiledClassNode.java:230)
at org.codehaus.groovy.ast.decompiled.DecompiledClassNode.createMethodNode(DecompiledClassNode.java:236)
at org.codehaus.groovy.ast.decompiled.DecompiledClassNode.lazyInitMembers(DecompiledClassNode.java:203)
at org.codehaus.groovy.ast.decompiled.DecompiledClassNode.getDeclaredMethods(DecompiledClassNode.java:122)
at org.codehaus.groovy.ast.ClassNode.getDeclaredMethods(ClassNode.java:851)
at org.codehaus.groovy.ast.ClassNode.getMethods(ClassNode.java:866)
at org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticMethod(ClassNodeUtils.java:233)
at org.codehaus.groovy.ast.ClassNode.hasPossibleStaticMethod(ClassNode.java:1352)
at org.codehaus.groovy.control.StaticImportVisitor.findStaticMethod(StaticImportVisitor.java:557)
at org.codehaus.groovy.control.StaticImportVisitor.findStaticMethodImportFromModule(StaticImportVisitor.java:499)
at org.codehaus.groovy.control.StaticImportVisitor.transformMethodCallExpression(StaticImportVisitor.java:252)
at org.codehaus.groovy.control.StaticImportVisitor.transform(StaticImportVisitor.java:133)
at org.codehaus.groovy.ast.expr.Expression.transformExpressions(Expression.java:49)
at org.codehaus.groovy.ast.expr.ArgumentListExpression.transformExpression(ArgumentListExpression.java:66)
at org.codehaus.groovy.control.StaticImportVisitor.transform(StaticImportVisitor.java:142)
at org.codehaus.groovy.control.StaticImportVisitor.transformMethodCallExpression(StaticImportVisitor.java:248)
at org.codehaus.groovy.control.StaticImportVisitor.transform(StaticImportVisitor.java:133)
at org.codehaus.groovy.ast.ClassCodeExpressionTransformer.visitExpressionStatement(ClassCodeExpressionTransformer.java:108)
at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:40)
at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:86)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:164)
at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:69)
at org.codehaus.groovy.ast.ClassCodeExpressionTransformer.visitIfElse(ClassCodeExpressionTransformer.java:121)
at org.codehaus.groovy.ast.stmt.IfStatement.visit(IfStatement.java:41)
at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:86)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:164)
at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:69)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:138)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:111)
at org.codehaus.groovy.ast.ClassCodeExpressionTransformer.visitConstructorOrMethod(ClassCodeExpressionTransformer.java:66)
at org.codehaus.groovy.control.StaticImportVisitor.visitConstructorOrMethod(StaticImportVisitor.java:108)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:106)
at org.codehaus.groovy.ast.ClassNode.visitMethods(ClassNode.java:1100)
at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1093)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:52)
at org.codehaus.groovy.control.CompilationUnit.lambda$addPhaseOperations$3(CompilationUnit.java:209)
at org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:942)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:671)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:635)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:389)
at groovy.lang.GroovyClassLoader.lambda$parseClass$3(GroovyClassLoader.java:332)
at org.codehaus.groovy.runtime.memoize.StampedCommonCache.compute(StampedCommonCache.java:163)
at org.codehaus.groovy.runtime.memoize.StampedCommonCache.getAndPut(StampedCommonCache.java:154)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:330)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:314)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:244)
at net.grinder.scriptengine.groovy.GroovyScriptEngine.<init>(GroovyScriptEngine.java:64)
at net.grinder.scriptengine.groovy.GroovyScriptEngineService.createScriptEngine(GroovyScriptEngineService.java:87)
at net.grinder.engine.process.ScriptEngineContainer.getScriptEngine(ScriptEngineContainer.java:105)
at net.grinder.engine.process.GrinderProcess.run(GrinderProcess.java:345)
at net.grinder.engine.process.WorkerProcessEntryPoint.run(WorkerProcessEntryPoint.java:87)
at net.grinder.engine.process.WorkerProcessEntryPoint.main(WorkerProcessEntryPoint.java:60)
1 error
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:295)
at org.codehaus.groovy.control.ErrorCollector.addException(ErrorCollector.java:143)
at org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:976)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:671)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:635)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:389)
at groovy.lang.GroovyClassLoader.lambda$parseClass$3(GroovyClassLoader.java:332)
at org.codehaus.groovy.runtime.memoize.StampedCommonCache.compute(StampedCommonCache.java:163)
at org.codehaus.groovy.runtime.memoize.StampedCommonCache.getAndPut(StampedCommonCache.java:154)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:330)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:314)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:244)
at net.grinder.scriptengine.groovy.GroovyScriptEngine.<init>(GroovyScriptEngine.java:64)
... 5 common frames omitted
대충 jdk17이 문제라는 것 같다.
그래서 구글링을 더 해보니 jdk11까지 지원된다는 모양...제대로 에러된 것 같으니 jdk-11로 변경하고 다시 시도해보려고 했는데........무슨 짓을 해도 java 버전이 안 바뀐다...왜???
JDK-17을 날리고 JDK-11만 설치하고 컴퓨터를 재부팅해도 안 된다....나중에 노트북으로 다시 해봐야 할 듯 싶다
이것 저것 다 해보다가 제어판에서 jdk 17을 삭제했더니 드디어 jdk 11로 바뀐다
DateTime
- 테스트가 실행된 날짜와 시간
-> 이를 통해 특정 시간대에 성능이 저하되는 문제를 식별할 수 있음
vuser
- 가상 사용자(가상 사용자 수)
- 가상 사용자는 시뮬레이션된 동시 사용자 수를 나타냄
-> 시스템이 특정 부하 수준에서 어떻게 동작하는지 이해하는데에 필수적
Tests
- 실행된 테스트의 총 수
Errors
- 발생한 오류의 수
-> 오류가 많을 수록 당연히 시스템의 안정성이 떨어진다는 것
Mean_Test_Time_(ms)
- 테스트의 평균 실행 시간(밀리초)
-> 시스템의 반응 시간
==> 사용자가 웹 페이지를 요청하고 응답을 받는데 걸리는 시간 측정 가능
Test_Time_Standard_Deviation_(ms)
- 테스트 실행 시간의 표준 편차(밀리초)
- 실행 시간의 분산을 측정
-> 시스템의 일관성을 이해하는데 필요
TPS
- 초당 트랜잭션 수(TPS)
- 시간 당 처리된 트랜잭션의 평균 수를 나타냄
-> 시스템의 처리량 파악
Mean_response_length
- 응답의 평균 길이
-> 클라이언트가 받는 데이터량
Response_bytes_per_second
- 초당 응답 바이트 수
- 단위 시간당 전송된 바이트의 평균을 나타냄(시스템이 한번에 보낼 수 있는 데이터량)
-> 네트워크 대역폭을 이해하고 시스템의 데이터 처리 속도를 평가하는데 필요
-> 대역폭이 제한되면 낮아질 수 있음
ex) 웹 서버가 초당 응답하는 데이터 양이 제한된 네트워크 대역폭을 초과하면, 사용자는 웹 페이지를 로드하는 데 더 많은 시간이 걸릴 것
Response_errors
- 응답 오류의 수
Mean_time_to_resolve_host
- 호스트 이름을 IP주소로 변환하는데 소요된 평균 시간
-> DNS 해결 시간(네트워크 구성에 문제가 있으면 호스트 해결 시간이 길어질 수 있음)
Mean_time_to_establish_connection
- 연결 설정까지의 평균 시간
-> 네트워크 지연을 이해하고 연결 설정 시간을 최적화할 때 필요(서버 부하가 높을 수록 연결 설정 시간이 길어질 수 있음)
Mean_time_to_first_byte
- 첫 번째 바이트까지의 평균 시간
- 호스트에 요청을 보내고 첫 번째 바이트를 수신하는 데 소요된 평균 시간을 측정
-> 서버 응답 시간을 이해하고 성능을 평가할 때 필요(길어질 수록 사용자는 웹 페이지가 느리게 로딩된다고 느껴질 것)
실행 성공
실행 환경:
1. 서버컴퓨터 JDK-11사용 Gradle 프로젝트 / 테스트를 실행한 컴퓨터 JDK-11 Gradle설치
2. 서버컴퓨터 JDK-17사용 Maven 프로젝트 / 테스트를 실행한 컴퓨터 JDK-11 Gradle설치
기존에 nGrinder가 작동되지 않은 이유는 JDK-11를 사용하면서, Gradle이 설치되지 않았기 때문인 것 같다.
(확실하게 검증되지는 않았다....)
스크립트는 Groovy로 짜야한다고 너무 두려워하지 않아도 되었다.
대부분은 알아서 짜준다
이런 식으로 입력하고 만들기를 누르면 알아서 Groovy로 짜준다.
import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.script.GTest
import net.grinder.script.Grinder
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.ngrinder.http.HTTPRequest
import org.ngrinder.http.HTTPRequestControl
import org.ngrinder.http.HTTPResponse
import org.ngrinder.http.cookie.Cookie
import org.ngrinder.http.cookie.CookieManager
@RunWith(GrinderRunner) // Junit에 테스트를 GrinderRunner를 사용함을 고지
class TestRunner {
public static GTest test // 테스트 설정
public static HTTPRequest request
public static Map<String, String> headers = [:]
public static Map<String, Object> params = [:]
public static List<Cookie> cookies = []
@BeforeProcess // 각 테스트의 프로세스 실행 전에 실행
public static void beforeProcess() {
HTTPRequestControl.setConnectionTimeout(300000) // 타임아웃 지정
test = new GTest(1, "121.122.4.123") // 첫 번째 Test 지정
request = new HTTPRequest()
// 파라미터 지정
params.put("Key1", "Value1")
params.put("Key2", "Value2")
grinder.logger.info("before process.")
}
@BeforeThread // 각 테스트의 스레드 실행 전에 실행
public void beforeThread() {
test.record(this, "test") // 통계를 위해 테스트 지정
grinder.statistics.delayReports = true
grinder.logger.info("before thread.")
}
@Before // 각 테스트의 메소드 실행 전에 실행
public void before() {
request.setHeaders(headers) // 헤더 설정
CookieManager.addCookies(cookies) // 쿠키 설정
grinder.logger.info("before. init headers and cookies")
}
@Test // 테스트 실행
public void test() {
HTTPResponse response = request.GET("http://121.122.4.123:8080/projectName/", params)
// 요청 방식, url, 파라미터 지정
if (response.statusCode == 301 || response.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", response.statusCode)
} else {
assertThat(response.statusCode, is(200))
}
}
}
테스트를 추가하고 싶으면 아래의 내용을 각각 추가하면 된다.
이하는 Test2를 추가하는 경우
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static GTest test2; ////////////////////////
@BeforeProcess
public static void beforeProcess() {
test = new GTest(1, "123.123.0.123")
test2 = new GTest(2, "123.123.0.123") ////////////////////////////
// Set param2 data ///////////////////////(선택 사항)
params2.put("userId", "b@b")
params2.put("userPw", "asdasd")
params2.put("userPw", "asdasd")
params2.put("nickname", "bbbb")
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
test.record(this, "test2") /////////////////////////////
}
@Test
public void test() {
}
@Test ////////////////////////////////////////////////////
public void test2() {
HTTPResponse response = request.POST("http://123.123.0.123:8090/projectName2", params2)
if (response.statusCode == 301 || response.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", response.statusCode)
} else {
assertThat(response.statusCode, is(200))
}
}
}
잘 만들어졌을 경우에는 이렇게 출력된다.
그러면 간단한 로그인 기능과 회원가입 기능을 실행시켜보았다.
가상 유저는 3000
한 건의 에러 빼고는 모두 성공했다.
회원가입 기능은 동일값이 들어가지 않게 했기 때문에 빠르게 실패처리되어서 부하가 덜한듯 하다.
서버 콘솔에서는 이렇게 출력된다.
마치 TestCode를 돌릴 때 같다.
이대로는 제대로 된 데이터를 얻기 어렵기 때문에 log.info()를 써서 출력하는 편이 좋다.
우측 상단의 시스템 설정을 통해 제약 해제 / 강화도 가능하다
결론
- 에이전트의 메모리/CPU와 스크립트가 얼마나 무거운지에 따라 수용할 수 있는 vuser가 다르다
- vuser가 많거나 / 에이전트가 많거나 / 메모리가 부족하거나 / 스크립트가 무거움에 따라 과부하 정도가 다르다.
--> 테스트 시나리오를 작성한 다음, think time을 넣어 더 많은 사용자를 받을 수 있도록 해야 될 것 같습니다.