애플리케이션을 배포한 이후, 전반적인 성능과 개선사항을 확인하기 위해 부하 테스트를 진행하기로 했다. 다양한 부하 테스트 도구들 중에서도 네이버 오픈소스 도구이자 Groovy 문법을 사용해 많은 레퍼런스를 찾을 수 있는 nGrinder를 선택했다.
설치 (Windows)
1. nGrinder war 파일 설치
https://github.com/naver/ngrinder/releases에서 war 파일을 설치한다
2. cmd 명령프롬프트에서 실행
java -jar ngrinder-controller-버전.war --port=임의포트번호
3. localhost:포트번호 주소 입력해 브라우저 접속, 로그인
디폴트 아이디, 비밀번호 : admin, admin
4. 에이전트 다운로드
메뉴의 에이전트 다운로드 클릭, 설치 후 명령 실행
tar -xvf ngrinder-agent-버전-localhost.tar //다운로드한 파일 압축 해제
cd ngrinder-agent //생성된 문서로 이동
run_agent.bat //에이전트 실행
4. 실행되는 에이전트 확인
에이전트 관리 메뉴에 들어가면 실행되는 에이전트를 확인할 수 있다!
테스트 스크립트 작성 (오류 발생)
기본적인 설정이 완료됐으면 테스트 스크립트를 작성하면 된다. 기본적인 템플릿은 주어져서 다른 레퍼런스들을 참고하면서 테스트를 작성했다.
그런데 계속 POST 생성 요청 (요청 바디에서 데이터 가져와 static 변수에 대입) 메소드들에서 400 오류가 발생한다.. 문법적인 오류인 것 같아 여러 사이트들 참조하면서 변경을 계속 해봤는데도 오류는 그대로다. 이번주까지 해결해보고 안 되면 GET 요청들 위주로 먼저 테스트해봐야겠다.
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
import groovy.json.JsonSlurper
/**
* 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 {
def toJSON = { new JsonSlurper().parseText(it) }
public static GTest test1
public static GTest test2
public static GTest test3
public static GTest test4
public static GTest test5
public static GTest test6
public static GTest test7
public static GTest test8
public static HTTPRequest request
public static Map<String, String> headers = [:]
public static Map<String, Object> params = [:]
public static List<Cookie> cookies = []
private static String accessToken;
private static String refreshToken;
private static String room;
private static String dajim;
@BeforeProcess
public static void beforeProcess() {
HTTPRequestControl.setConnectionTimeout(300000)
test1 = new GTest(1, "127.0.0.1")
test2 = new GTest(2, "127.0.0.1")
test3 = new GTest(3, "127.0.0.1")
test4 = new GTest(4, "127.0.0.1")
test5 = new GTest(5, "127.0.0.1")
test6 = new GTest(6, "127.0.0.1")
test7 = new GTest(7, "127.0.0.1")
test8 = new GTest(8, "127.0.0.1")
request = new HTTPRequest()
grinder.logger.info("before process.")
}
@BeforeThread
public void beforeThread() {
test1.record(this, "test1")
test2.record(this, "test2")
test3.record(this, "test3")
test4.record(this, "test4")
test5.record(this, "test5")
test6.record(this, "test6")
test7.record(this, "test7")
test8.record(this, "test8")
grinder.statistics.delayReports = true
grinder.logger.info("before thread.")
}
@Before
public void before() {
headers.put("Content-Type", "application/json")
headers.put("Authorization", "Bearer " + accessToken)
request.setHeaders(headers)
grinder.logger.info("before. init headers and cookies")
}
@Test
public void test1() { //로그인
def reqBody = "{\"id\":\"id123@gmail.com\",\"pw\":\"123\"}";
HTTPResponse response = request.POST("http://127.0.0.1:8080/auth/login", reqBody.getBytes())
def result = response.getBody(toJSON);
accessToken = result.accessToken
refreshToken = result.refreshToken
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))
}
}
@Test
public void test2() { //챌린지 생성
def reqBody2 = "{\"name\": \"명상\", \"category\": \"MINDFULNESS\", \"reward\": \"\", \"passCount\": 10, \"startDate\": \"2023-01-11\", \"totalDays\": 10, \"type\": \"SINGLE\", \"successCount\": 0, \"usedPassCount\" : 0}";
def reqBody = '{"name": "명상", "category": "MINDFULNESS", "reward": "", "passCount": 10, "startDate": "2023-01-11", "totalDays": 10, "type": "SINGLE", "successCount": 0, "usedPassCount" : 0}';
HTTPResponse response = request.POST("http://127.0.0.1:8080/challenge", reqBody.getBytes(), headers);
def result = response.getBody(toJSON);
room = result.roomNumber.toString();
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))
}
}
@Test
public void test3() { //다짐 생성
def reqBody = "{\"content\":\"내용\",\"open\":\"PUBLIC\"}"
def reqBody2 = '{"content":"내용","open":"PUBLIC"}'
HTTPResponse response = request.POST("http://127.0.0.1:8080/challenge/"+room+"/dajim", reqBody2.getBytes(), headers)
def result = response.getBody(toJSON);
dajim = result.dajimNumber.toString()
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))
}
}
@Test
public void test4() { //피드 조회
HTTPResponse response = request.GET("http://127.0.0.1:8080/feed")
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))
}
}
@Test
public void test5() { //이모션 생성
def reqBody = "{\"dajimNumber\": $dajim, \"sticker\": \"WATCH\"}"
def reqBody2 = '{"dajimNumber": '+dajim+', "sticker": "WATCH"}'
HTTPResponse response = request.POST("http://127.0.0.1:8080/feed/emotion", reqBody2.getBytes(), headers)
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))
}
}
@Test
public void test6() { //챌린지 조회
HTTPResponse response = request.GET("http://127.0.0.1:8080/challenge/list")
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))
}
}
@Test
public void test7() { //회원정보 조회
HTTPResponse response = request.GET("http://127.0.0.1:8080/user/details")
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))
}
}
@Test
public void test8() { //로그아웃
HTTPResponse response = request.POST("http://127.0.0.1:8080/auth/logout")
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))
}
}
}
Tests Errors Mean Test Test Time TPS Mean Response Response Mean time to Mean time to Mean time to
Time (ms) Standard response bytes per errors resolve host establish first byte
Deviation length second connection
(ms)
Test 1 1 0 257.00 0.00 2.42 365.00 883.78 0 0.00 11.00 27.00 "127.0.0.1"
Test 2 0 1 NaN 0.00 0.00 NaN 0.00 0 NaN NaN NaN "127.0.0.1"
Test 3 0 1 NaN 0.00 0.00 NaN 0.00 0 NaN NaN NaN "127.0.0.1"
Test 4 1 0 14.00 0.00 2.42 98.00 237.29 0 0.00 1.00 12.00 "127.0.0.1"
Test 5 0 1 NaN 0.00 0.00 NaN 0.00 0 NaN NaN NaN "127.0.0.1"
Test 6 1 0 15.00 0.00 2.42 497.00 1203.39 0 0.00 1.00 14.00 "127.0.0.1"
Test 7 1 0 11.00 0.00 2.42 126.00 305.08 0 0.00 0.00 0.00 "127.0.0.1"
Test 8 1 0 24.00 0.00 2.42 0.00 0.00 0 0.00 0.00 0.00 "127.0.0.1"
Totals 5 3 64.20 96.50 12.11 217.20 2629.54 0 0.00 2.60 10.60
'Programming' 카테고리의 다른 글
[MSSQL] MSSQL 에이전트 작업 알림 메일 (SMTP) 설정하기 (0) | 2024.03.23 |
---|---|
[프로그래머스/자바] 점프와 순간 이동 (0) | 2024.02.04 |
[Git] Head? Stage? Branch? (0) | 2023.05.15 |
NoSQL에 대한 대략적 정리 (0) | 2023.03.13 |
소프트웨어 아키텍처, 도메인 (0) | 2023.03.09 |