본문 바로가기

Programming/Spring

nGrinder 부하 테스트

오늘은 제가 진행했던 프로젝트의 성능을 측정하기 위해 네이버에서 개발한 오픈소스를 사용 해보도록 하겠습니다.

nGrinder는 Java기반 성능 테스트 도구입니다.

 

우선 nGrinder를 사용하기 위해서는 docker가 필요합니다.

docker pull ngrinder/controller

 

그 다음에 도커를 실행 하기 위해 다음과 같은 명령어를 입력합니다.

 

docker run -d \
  --platform linux/amd64 \
  -v ~/ngrinder-controller:/opt/ngrinder-controller \
  --name controller \
  -p 8081:80 \
  -p 16001:16001 \
  -p 12000-12009:12000-12009 \
  ngrinder/controller

 

Agent도 설치 해줍니다.

Agent는 스크립트를 실행 시키게 도와줍니다.

 

docker pull ngrinder/agent

 

그러면 이제 이런 형식으로 확인 할 수 있다.

 

http://localhost:8081/home 에 접근 할 수 있게 되는데

여기서 로그인 할 때는 admin,admin으로 접근 하면 된다.

 

 

다음으로 테스트를 돌릴 스크립트를 만들어야 한다.

 

스크립트는 Groovy, Jython으로 작성할 수 있다. Groovy는 자바와 유사하고 Jython은 파이썬과 유사하다.

 

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 Map<String, Object> params = [:]
	public static List<Cookie> cookies = []

	@BeforeProcess
	public static void beforeProcess() {
		HTTPRequestControl.setConnectionTimeout(300000)
		test = new GTest(1, "Test1")
		request = new HTTPRequest()
		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("https://url", params)

		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))
		}
	}
}

 

 

이런 형식으로 만든다음 performance Test를 통해서 해당 스크립트를 실행 할 수 있다.

 

나의 경우 사용자가 책을 읽을 때 마다 실행 하는 북마크를 생성 하는데 그 북마크의 성능을 테스트 해보았다.

 

TPS의 성능이 높게 나왔었다. 대신 에러율이 많이 나오게 되서

코드를 살짝 변경했다.

 

북마크를 다루는 코드를 여러개의 메소드로 불리해서 진행을 했었는데 TPS가 높게 나오긴 했지만, 에러율이 많이 높았었다.

 

그래서 코드를 리팩토링을 진행 했다.

 

Optional<BookMarkDAO> optionalBookMark = bookMarkRepositroy.findByNicknameAndBookid(bookMarkDAO.getNickname(), bookMarkDAO.getBookid());

        if (optionalBookMark.isPresent()) {
            BookMarkDAO bookMark = optionalBookMark.get();



            // 현재 페이지가 더 크다면 페이지 업데이트
            bookMark.setPage(bookMarkDAO.getPage());
            bookMarkRepositroy.save(bookMark);  // 책갈피 정보 업데이트
            //System.out.println(page+" "+bookMarkDAO.getPage()+"어떻게 되는지좀 보자");
            if(page >= bookMarkDAO.getPage()){
                boolean flag = completBookRepository.existsByNicknameAndBookid(bookMarkDAO.getNickname(), bookMarkDAO.getBookid());
                if(!flag){
                    CompletBookDAO newCompletBook = CompletBookDAO.builder()
                            .nickname(bookMarkDAO.getNickname())
                            .bookid(bookid)
                            .build();
                    completBookRepository.save(newCompletBook);
                }
                else{
                    //System.out.println("이미 존재함");
                }

                // System.out.println("너 작동 안하니?");
            }

        } else {
            // 책갈피 정보가 존재하지 않으면 새로 저장
            BookMarkDAO newBookMark = BookMarkDAO.builder()
                    .nickname(bookMarkDAO.getNickname())
                    .bookid(bookMarkDAO.getBookid())
                    .page(bookMarkDAO.getPage())
                    .build();

            bookMarkRepositroy.save(newBookMark);  // 새로운 책갈피 정보 저장
        }


테스트 결과

TPS나  호출의 빈도수는 비슷하나 Error의 수는 줄게 되었다.

 

하지만 근본적으로 요청이 많아지면 서버가 다운이 된다. 이 문제를 해결 해봐야 겠다.

 

'Programming > Spring' 카테고리의 다른 글

EntityManager란?  (0) 2025.04.13
Spring 성능 부하 테스트 Thread Pool  (0) 2024.12.13
[JPA] 랜덤값 가져오기  (0) 2024.09.29
[Project] Builder 패턴 사용  (0) 2024.08.21
Spring Boot JPA를 이용한 수정하기 PUT  (0) 2024.01.08