티스토리 뷰
성능 테스트 도구 비교
Gatling
- 스칼라를 통해 테스트 스크립트를 생성하는 부하 테스트 도구
- 비동기식 아키텍처로 가상 사용자를 스레드가 아닌 메시지로 생성해 수천 명의 동시 사용자 재현가능한 성능 좋은 테스트 도구
- 분산 테스트 지원하지 않아 다 수의 컴퓨터를 통해 테스트 할 수 없음..
JMeter
- GUI를 제공해 쉽게 사용해 볼 수 있다.
- 한 명의 가상 사용자를 하나의 스레드로 생성하여 동시성제한 있음.
- 분산 테스트를 지원한다. 즉, 여러대의 컴퓨터를 통해 테스트하고 그 테스트 결과를 종합할 수 있다.
nGrinder
- Jython, Groovy 스크립트를 활용해 테스트 시나리오 작성가능
- 한 명의 가상 사용자를 하나의 스레드로 생성하여 동시성제한 있음.
- 분산 테스트를 지원한다. 즉, 여러대의 컴퓨터를 통해 테스트하고 그 테스트 결과를 종합할 수 있다.
특징 비교
Gatling | nGrinder | JMeter | |
요청 처리 방법 | 비동기 지원 | 비동기 지원하지 않음 | 비동기 지원하지 않음 |
분산 테스트 지원 | 분산테스트 지원하지 않음 | 분산테스트 지원 | 분산테스트 지원 |
도구 선택
nGrinder , Gatling 학습을 목적으로 둔 성능 테스트다 보니 특징이 서로 다른 두 도구 모두 사용해보고자 함.
다만 우선적으로는 nGinder 를 선택하고자 함. F-Lab 멘토링을 같이 받고있는 동료분이 Gatling 을 사용하기로 하셔서 서로 다른 도구를 사용해 공유하는 것이 좋아보이기 때문인데. 추후 관련 내용을 공유받고 직접 한 번 시도해볼 예정.
nGrinder
nGrider 구조
Controller
- 컨트롤러는 Ngrinder의 GUI 를 제공해주는 부분을 말한다.
- 스크립트 생성 및 테스트 명령을 Agent 에 전달하는 역할
Agent
- Controller 로 부터 요청을 받아 Target 에게 요청을 보내는 역할을 한다.
Target
- Agent로 부터 요청을 받아 스트레스 테스트를 해야하는 대상
작업 과정
우선 Ngrinder 를 통한 스트레스 테스트를 하기 위해서는 요청을 위한 스크립트 생성및 요청을 에이전트로 전달해주는 컨트롤러와 전달 받은 요청에 따라 스트레스 테스트 타겟 서버에 스트레스 테스트를 실행해줄 에이전트가 필요하다.
Controller 다운로드 및 실행
ngrinder-controller-{version}.war 를 위 링크에서 다운로드 받는다.
위에서 다운받은 파일이 있는 폴더에서 해당 명령어를 통해 실행한다.
java -XX:MaxPermSize=200m -jar ngrinder-controller-3.5.3.war -p {port}
⛔️ 주의
- 자바 8 or 11 로 실행하는 것을 추천함. 자바 17에서는 불안정하다.
Controller 접속
브라우저에서 주소창에 'https://localhost:{port}' 를 입력하여 nGrinder Controller에 접속후
위 화면에서 아이디 : admin , 패스워드 : admin 을 입력하고 지원하는 언어를 한국어로 하여 로그인한다.
Agent 다운로드 및 실행
Controller에 로그인하고 나면 우측 상단에 에이전트 다운로드를 찾아서 다운받을 수 있다.
해당파일의 압축은 tar -xvf {agent-file} 을 통해 풀 수 있다. 압축을 풀고나면 ngrinder-agent 폴더로 들어가 run_agent.sh 쉘 파일을 실해해주면 에이전트가 실행된다.
./run_agent.sh
💡 만약 환경이 Local 환경이 아니라면 agent_conifg 파일을 수정해주어야 한다.
스크립트 만들기
nGrinder는 스크립트를 통해 테스트 시나리오를 만들 수 있다. nGrinder의 Controller 에는 스크립트를 만드는 기능을 제공한다.
스크립트 탭에서 스크립트 만들기를 선택하면 아래와 같은 화면이 나온다. 해당 화면에서
URL, 헤더, Body 등을 입력하여 만들어줄 수 있다.
⛔️ 주의
- Local 환경의 테스트를 진행할 때 URL에 localhost 를 추가하면 적절한 형식이 아니라는 문구가 나온다. 이 때는 127.0.0.1 로 대신하면 생성이 가능해진다.
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 static net.grinder.util.GrinderUtils.* // You can use this if you're using nGrinder after 3.2.3
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
/**
* A simple example using the HTTP plugin that shows the retrieval of a single page via HTTP.
*
* This script is automatically generated by ngrinder.
*
* @author admin
*/
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
public static Map<String, String> headers = [:]
public static String body = "{\\n \\"groupOrderId\\":null,\\n \\"itemId\\":null,\\n \\"quantity\\":4,\\n \\"hours\\":24,\\n \\"sendInfo\\":{\\n \\"senderName\\": \\"손현호\\",\\n \\"senderPhoneNumber\\":\\"01076181227\\"\\n },\\n \\"recipientInfo\\":{\\n \\"recipientName\\":\\"고영찬\\",\\n \\"firstAddress\\":\\"인천시\\",\\n \\"secondAddress\\":\\"주월동\\",\\n \\"zipCode\\":\\"322-4\\",\\n \\"recipientPhoneNumber\\":\\"010-xxxx-xxxx\\"\\n }\\n}"
public static List<Cookie> cookies = []
@BeforeProcess
public static void beforeProcess() {
HTTPRequestControl.setConnectionTimeout(300000)
test = new GTest(1, "127.0.0.1") //IP
request = new HTTPRequest()
// Set header data
headers.put("Content-Type", "application/json")
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.POST("<http://127.0.0.1:8072/orders>", body.getBytes())
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(201))
// 예상하는 Status Code 정확히 맞춰야함
// Create에 대해 200번으로 해서 검증 실패 나왔었음.**
}
}
}
테스트 생성
상단에 성능테스트 탭을 통해 테스트 생성이 가능하다. 테스트명및 기본 설정을 하고 스크립트를 선택하여 테스트를 시작할 수 있다. 실행중인 에이전트가 없다면 테스트 생성이 불가능하다.
테스트 결과
Test 정보
Total Vuser(Virtual User) : 가상 유저수
TPS : 초당 트랜잭션 수 즉, 초당 요청 개수
평균 테스트 시간 : 테스트 한 건이 응답을 주는 시간
🤔 테스트 과정의 이슈
- 기본적으로 개인 PC에 기본 자바 버전이 17 버전이었다. 17 버전으로 nGrinder 를 시도했을 때 Agent 실행과정에서 아래와 같은 에러가 발생했다
- JNA는 (Java Native Access) JNI를 통하지 않고 원시 라이브러리에 액세스 하기 위한 라이브러리
- 로드할 수 없는 위치에 가보았을 때 jna 로 시작하는 다른 이름의 파일이 있었던 것으로 보아 17버전에 포함되지 않았거나, 위치가 바뀌었거나 이름이 바뀌었을 것으로 예상된다.
2022-10-04 22:20:31,642 INFO agent config: NGRINDER_AGENT_HOME : /Path/.ngrinder_agent
2022-10-04 22:20:31,643 INFO agent config: Overwrite the existing agent.conf with __agent.conf
Exception in thread "main" java.lang.UnsatisfiedLinkError: Can't load library: /Path/Library/Caches/JNA/temp/jna14880329978073017781.tmp
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2393)
at java.base/java.lang.Runtime.load0(Runtime.java:755)
at java.base/java.lang.System.load(System.java:1953)
at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1018)
at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:988)
at com.sun.jna.Native.<clinit>(Native.java:195)
at com.sun.jna.Structure.setAlignType(Structure.java:280)
at com.sun.jna.Structure.<init>(Structure.java:197)
at com.sun.jna.Structure.<init>(Structure.java:193)
at com.sun.jna.Structure.<init>(Structure.java:180)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.sun.jna.platform.mac.SystemB$Timeval.<init>(SystemB.java:511)
at oshi.software.os.mac.MacOperatingSystem.<clinit>(MacOperatingSystem.java:84)
at oshi.SystemInfo.createOperatingSystem(SystemInfo.java:111)
at oshi.util.Memoizer$1.get(Memoizer.java:87)
at oshi.SystemInfo.getOperatingSystem(SystemInfo.java:100)
at org.ngrinder.common.util.SystemInfoUtils.<clinit>(SystemInfoUtils.java:59)
at org.ngrinder.NGrinderAgentStarter.checkDuplicatedRun(NGrinderAgentStarter.java:280)
at org.ngrinder.NGrinderAgentStarter.main(NGrinderAgentStarter.java:208)
- Mac M1 (ARM) 의 경우 Ngrinder Docker Image가 존재하지 않는다.
작업 과정에서 느낀점
nGrinder를 활용한 스트레스 테스트 과정에서 테스트 시도까지는 어렵지 않았다. 다만 해당 과정에서 테스트의 목적과 전략 그리고 각 지표를 통해 어떤 가설을 세울 수 있을지 가 더 중요해보인다.
추후 확인해 봐야할 목록은 아래와 같다고 생각이 든다.
- TPS와 total vuser 를 통해 어떤 가설을 세울 수 있는가
- ramp-up 테스트가 의미하는 바가 무엇인가?
- 테스트 시간을 길었을 때 혹은 짧았을 때 각각은 어떤 의미를 갖는가.
- 바이트 도달 시간 혹은 평균 테스트 시간이 주는 의미는 무엇인가?
- 어떤 테스트 전략이 있고 각 테스트 전략에 따라 각 지표는 어떤 의미를 가지는가?
상황에 맞는 테스트 전략 세우기와 그 결과를 통해 가설을 만들어 낼 수 있도록 컴퓨터 사이언스 기초 지식학습이 중요해 보인다.
추가로 GET 요청에는 JSON BODY 를 넣을 수 없음. 물론 GET 요청에 바디를 추가하는 것이 정상적인 것은 아니지만, 종종 그런 방식으로 API를 작성한 경험이 있다. 하지만 이런 경우 외부 라이브러리 사용에 제약이 생길 수 있다는 점을 명심해야할 것으로 보인다.
- 외에도 안드로이드의 경우 GET에 Body를 넣는 경우 요청이 불가능한 경험이 있음
참조
'BackEnd' 카테고리의 다른 글
RebbitMQ VS Kafka 비교 분석하기 (1) | 2022.11.10 |
---|---|
Netty VS Tomcat 비교 분석하기 (0) | 2022.11.08 |
[MSA] 벌크헤드 패턴( Bulkhead Pattern ) (0) | 2022.09.18 |
Circuit breaker Pattern ( 회로 차단기 패턴 ) (0) | 2022.09.03 |
Fallback Pattern ( 대체 패턴 ) (0) | 2022.09.01 |
- Total
- Today
- Yesterday
- 육각형 아키텍처
- pool
- multimap
- 내부 단편화
- RestAssured
- 세션 불일치
- 클린 아키텍처
- Memory Fragmentation
- pass by reference
- java
- 수직 분할
- 컴포짓 패턴
- ATDD
- Sticky Session
- 객체 풀
- pass by value
- 장애 해결기
- OOP
- 정적 타입 언어
- SpringBoot 2.2
- 메모리 파편화
- 수평 분할
- 외부 단편화
- 동적 디스패치
- Object Pool
- 뾰족함
- 동적 타입 언어
- 메모리 단편화
- Session
- Clean Architecture
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |