티스토리 뷰

2019/01/23 - [Programming/Java] - Spring Boot 공식 지원 내장 WAS 인 Undertow 을 씁시다.


이전에 Spring Boot 에서 Embedded Tomcat 대신에 Undertow 을 사용하자고 글을 작성한 적이 있습니다. 글의 내용에서 Undertow 의 성능이 더 뛰어나다는 것을 근거로 삼았는데, 저의 경험을 이야기 한 것일 뿐이었기에 실제 간단한 코드를 이용해 테스트를 진행해 보려고 합니다.


  1. Spring Boot 프로젝트를 2 개(tomcat-pingpong, undertow-pingpong)  생성한다.  Java 11, war, Web 을 선택해서 생성한다.
  2. undertow-pingpong 에는 pom.xml 에서 Tomcat 을 제외하고 Undertow 을 활성화한다.
  3. 동일한 Controller 을 생성한다.
  4. 실행 시 Scouter 을 연동하여 서버의 상태를 모니터링 한다.


위와 같은 간단한 방법으로 진행합니다.먼저, undertow-pingpong 의 pom.xml 은 다음과 같이 수정합니다.


		<dependency>
			<groupid>org.springframework.boot</groupid>
			<artifactid>spring-boot-starter-web</artifactid>
			<exclusions>
				<exclusion>
					<artifactid>spring-boot-starter-tomcat</artifactid>
					<groupid>org.springframework.boot</groupid>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupid>org.springframework.boot</groupid>
			<artifactid>spring-boot-starter-undertow</artifactid>
			<scope>provided</scope>
		</dependency>


Web 선택 시 기본으로 Tomcat 이 의존성에 추가되는데, 이를 Undertow 로 변경합니다.


그리고, 두 프로젝트 모두에 아래와 같이 Controller 을 추가합니다.


@RestController
@RequestMapping("/test")
public class TestController {

	@GetMapping("/test")
	public Map test() {

		long currentTimestamp = System.currentTimeMillis();
		Random random = new Random(currentTimestamp);

		Map result = new HashMap<>();
		result.put("timestamp", currentTimestamp);
		result.put("random", random.nextLong());

		return result;

	}

	@GetMapping("/test2")
	public Callable> test2() {

		long currentTimestamp = System.currentTimeMillis();
		Random random = new Random(currentTimestamp);

		Map result = new HashMap<>();
		result.put("timestamp", currentTimestamp);
		result.put("random", random.nextLong());

		return () -> result;

	}

}


아주 간단한 내용이기 때문에 별다른 설명은 필요없을 겁니다. /test/test 혹은 /test/test2 호출 시 서버의 현재 시간을 microsecond 로 unixtimestamp 로 획득한 뒤, 이를 이용해서 난수를 발생시키고, 이 값들을 Json 으로 사용자에게 보내주는 것입니다.


실행 시에도 Scouter 연결을 제외하고는 별다른 옵션을 주지 않았습니다. 물론 tomcat-pingpong 의 경우에는 파일명만 다릅니다.


java -javaagent:"./agent.java/scouter.agent.jar" -Dscouter.config="./agent.java/conf/scouter.conf" -jar undertow-pingpong-0.0.1-SNAPSHOT.war


각 서버를 동시에 띄우지 않고 한 번씩 실행하였습니다. 테스트한 PC 는 HP ELITEDESK 800 제품이고, i7-8700 CPU @ 3.20GHz, 64.0GB, Windows 10 Pro 1809 가 설치되어 있습니다. Java 는 Zulu 11.0.2 입니다.

부하는 Apache HTTPd 을 Windows 에서 사용할 수 있도록 Build 된 공개 바이너리에서 ab(http://httpd.apache.org/docs/current/programs/ab.html) 을 따로 C:\temp 에 넣어두고 테스트 하겠습니다. 실행은 아래와 같은 옵션으로 진행하였습니다.


ab -n 100000 -c 1000 http://localhost:8080/test/test2


Scouter 는 같은 장비에 Windows 용 Docker 을 이용해서 내려받았습니다.


사실, 이렇게 한 장비에 몰아넣고 테스트하는게 좋은 건 아닌데, 간단한 테스트이므로 양해 부탁드립니다.



아래는 Scouter 에서 표시되는 결과입니다. 좌측이 Tomcat, 우측이 Undertow 입니다. 어림짐작으로 Tomcat 은 TPS 가 1200 대, Undertow 은 2400 대가 예상됩니다. 예전에 제가 테스트 할 때에는 좀 더 성능이 좋은 가상 머신에서 3 대의 VM 에 분산 호출을 하고 내부에서 좀 더 큰 작업을 하는 것을 추가해서 테스트 했기 때문에 XLog 상의 점들이 좀 더 극적으로 나타나서 TPS 가 아닌 XLog 상에서 확인이 바로 되었는데, 지금은 워낙 간단한 작업이다 보니 TPS 가 기준이 되어버렸네요.



ab 의 결과는 각각 아래와 같습니다.


Tomcat

C:\Temp>ab -n 100000 -c 1000 http://localhost:8080/test/test2

This is ApacheBench, Version 2.3 <$Revision: 1843412 $>

Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

Licensed to The Apache Software Foundation, http://www.apache.org/


Benchmarking localhost (be patient)

Completed 10000 requests

Completed 20000 requests

Completed 30000 requests

Completed 40000 requests

Completed 50000 requests

Completed 60000 requests

Completed 70000 requests

Completed 80000 requests

Completed 90000 requests

Completed 100000 requests

Finished 100000 requests



Server Software:

Server Hostname:        localhost

Server Port:            8080


Document Path:          /test/test2

Document Length:        56 bytes


Concurrency Level:      1000

Time taken for tests:   79.996 seconds

Complete requests:      100000

Failed requests:        53622

   (Connect: 0, Receive: 0, Length: 53622, Exceptions: 0)

Total transferred:      38117728 bytes

HTML transferred:       5643250 bytes

Requests per second:    1250.06 [#/sec] (mean)

Time per request:       799.960 [ms] (mean)

Time per request:       0.800 [ms] (mean, across all concurrent requests)

Transfer rate:          465.33 [Kbytes/sec] received


Connection Times (ms)

              min  mean[+/-sd] median   max

Connect:        0    0   0.4      0       6

Processing:   158  795  66.2    802     985

Waiting:        5  402 228.4    401     972

Total:        158  795  66.2    803     985


Percentage of the requests served within a certain time (ms)

  50%    803

  66%    816

  75%    824

  80%    831

  90%    853

  95%    870

  98%    912

  99%    969

 100%    985 (longest request)


Undertow

C:\Temp>ab -n 100000 -c 1000 http://localhost:8080/test/test2

This is ApacheBench, Version 2.3 <$Revision: 1843412 $>

Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

Licensed to The Apache Software Foundation, http://www.apache.org/


Benchmarking localhost (be patient)

Completed 10000 requests

Completed 20000 requests

Completed 30000 requests

Completed 40000 requests

Completed 50000 requests

Completed 60000 requests

Completed 70000 requests

Completed 80000 requests

Completed 90000 requests

Completed 100000 requests

Finished 100000 requests



Server Software:

Server Hostname:        localhost

Server Port:            8080


Document Path:          /test/test2

Document Length:        56 bytes


Concurrency Level:      1000

Time taken for tests:   75.989 seconds

Complete requests:      100000

Failed requests:        47555

   (Connect: 0, Receive: 0, Length: 47555, Exceptions: 0)

Total transferred:      28022319 bytes

HTML transferred:       5635457 bytes

Requests per second:    1315.97 [#/sec] (mean)

Time per request:       759.894 [ms] (mean)

Time per request:       0.760 [ms] (mean, across all concurrent requests)

Transfer rate:          360.12 [Kbytes/sec] received


Connection Times (ms)

              min  mean[+/-sd] median   max

Connect:        0    0   0.4      0       6

Processing:   148  754  67.8    776     890

Waiting:        7  383 217.3    380     884

Total:        148  754  67.8    776     891


Percentage of the requests served within a certain time (ms)

  50%    776

  66%    790

  75%    797

  80%    802

  90%    817

  95%    841

  98%    863

  99%    868

 100%    891 (longest request)


2019/02/22 - [Programming/Java] - 내장 Tomcat 와 외장 Tomcat 의 간단한 비교


댓글
  • 프로필사진 열공 안녕하세요~
    이전의 Undertow 에 대한 추천글에 이어서 직접 테스트 결과까지 올려주셔서 많은 참고 하였습니다.

    한가지 궁금한 점이 있어서 글을 남기는데요.
    Undertow 에 앱도 비동기 기반(WebFlux 등) 이어야 성능 이점이 있지 않을까 싶었는데,
    보여주신 결과로 보면 그렇진 않거든요.

    Spring MVC 앱에 WAS 만 Undertow 로 바꿨는데 성능적 이점이 생긴 이유를 알 수 있을까요?
    2020.03.01 18:40
  • 프로필사진 Favicon of https://zepinos.tistory.com zepinos 안녕하세요.

    이전 글(https://zepinos.tistory.com/35)에 잠깐 언급하였지만, "Netty 라는 아주 뛰어난 Java Network Library 을 이용해 새로운 Servlet Container 을 만들었습니다. 그것이 바로 Undertow 입니다." 과 같이 내부적인 동작이 Netty 기반으로 바뀌어서 속도가 빨라졌습니다. Netty 자체가 이벤트 기반 비동기 동작도 지원하기 때문에 fork 방식의 tomcat 보다 빠른 경우가 대부분입니다. 물론 WebFlux 같이 Netty 만을 이용할 경우 Servlet Container 기능조차 이용하지 않기 때문에 더 빠르겠죠. 또 WebFlux 에서도 Flux 나 Mono 쓰지 않고도 Return 가능합니다.
    2020.03.02 17:33 신고
  • 프로필사진 열공 친절한 답변 감사합니다~

    Optional 한 부분이 참 마음에 드네요.
    당장 WebFlux 로 가기 부담스러운 부분이 있으니, MVC 기반으로 하고 부분적으로 Mono/Flux 적용하는 방향으로 진행해 봐야 겠네요.

    덕분에 많은 도움 되었습니다!!
    2020.03.03 18:57
  • 프로필사진 Favicon of https://ssaemo.tistory.com Hojong 안녕하세요, 앞뒤 포스트와 함께 글 잘 읽었습니다 많은 도움이 되었습니다
    글을 읽다가 한 가지 질문이 생겨 댓글로 남깁니다
    scouter의 TPS(초당 Transaction인가요?)는 tomcat과 undertow에 명백한 차이가 있어보이는데,
    ab의 출력 결과에서는 undertow와 tomcat의 성능 차이가 보이지 않네요. ab의 출력 결과에서도 둘의 성능 차이를 알 수 있을만한 지표가 있나요 ??
    좋은 글 감사합니다
    2020.04.30 23:49 신고
댓글쓰기 폼