테스트 목적
테스트 환경
테스트 기대값
테스트 결과
리뷰
정해진 스레드 풀안에서 요청마다 스레드를 할당하여 동기적으로 처리하는 톰캣과
비교적 소수의 스레드풀에서 요청에 대해 스레드를 전환하며 비동기로 처리하는 네티를
기반으로 하는 두 스프링 서버의 상황에 따른 부하 성능을 구해보고 싶었다.
서버
| - | 톰캣 | 네티 | MySQL |
|---|---|---|---|
| 실행 환경 | Docker | Docker | 호스트 OS |
| 성능 | cpu 한 개로 제한 | CPU 한 개로 제한 | 제한 없음 |
부하 프로그램
Jmeter
테스트에 있는 모든 프로그램이 로컬 PC에서 실행되므로
현 테스트안에서 통신간 발생하는 네트워크 비용은 고려하지 않았다.
적은 양의 부하일 경우 즉 톰캣과 네티 모두 정해진 스레드풀보다 적은 양의 요청에서는
톰캣이 조금 유리하지 않을까 생각함
적은 양의 요청에서도 스레드 전환이 일어나는 네티에 비해 톰캣은 스레드 전환이 없으므로
스레드 전환에서 발생하는 비용때문에 톰캣이 조금 우수할 수 있음
많은 양의 부하에서는 네티가 우수할 것으로 보임
톰캣은 부하가 정해진 스레드풀을 벗어나면 나머지 작업들이 처리되지 못하고 대기큐에 잡히는 반면
네티는 부하가 많아지더라도 스레드를 비동기로 처리하여 많은 양의 작업을 비동기로 처리할 수 있음
또한 MySQL과의 통신에서도 톰캣은 통신사이의 지연간 스레드가 블로킹 되지만 네티는 지연이 발생하면 다른 작업으로 스레드가 전환되므로 지연에서도 유리할 것으로 보임
| 설정 | 값 |
|---|---|
| 스레드 그룹 | 1000 |
| 램프업 | 0 |
| 반복 | 10 |
Tomcat
TPS : 3064.8
Netty
TPS : 3947.6
1차 분석
더 많이 테스트를 했었지만 편차가 매우 컸음 CPU 사용량의 제한을 두고 테스트를 했었지만 테스트별 CPU 사용률의 진폭이 컸음
| 설정 | 값 |
|---|---|
| 스레드 그룹 | 1000 |
| 램프업 | 0 |
| 반복 | 100 |
Tomcat
TPS : 2941.8
Netty
TPS : 3897.6
2차 분석
테스트중 많은 부하를 걸다보니 Jmeter자체의 에러가 생기기도 했지만 서버 자체에서의 에러는 없었음
CPU 사용량의 진폭이 크고 테스트 TPS의 차이가 좀 났지만 전체적으로 Netty의 TPS가 더 높게 나온다.
이 API는 요청 시 상당한 양의 for문 연산을 부여하여 블로킹과 논블로킹 방식의 차이점을 확인 하기 위한 API 이다.
Tomcat
@GetMapping("/calculator")
public String calculate() {
tomCatService.calculate();
return "Calculate done";
}
Netty
@GetMapping("/calculator")
public Mono<String> calculate() {
nettyService.calculate();
return Mono.just("calculate done");
}
.calculate() 메소드
public void calculate() {
long num = 0;
for(long i = 0; i < 1000000000; i++) {
num += i;
}
}
/*---------------*/
public void calculate() {
Mono.fromRunnable(() -> {
long num = 0;
for (long i = 0; i < 1000000000; i++) {
num += i;
}
});
}
| 설정 | 값 |
|---|---|
| 스레드 그룹 | 100 |
| 램프업 | 10 |
| 반복 | 1 |
Tomcat
TPS : 4.1
Netty
TPS : 10.1
1차 분석
해당 테스트는 최대 10 TPS가 나올 수 있게 세팅되어있는데 Tomcat은 4.1, Netty는 10.1이 나오면서 Tomcat 보다 Netty가 높은 TPS 가져왔다.
| 설정 | 값 |
|---|---|
| 스레드 그룹 | 100 |
| 램프업 | 0 |
| 반복 | 10 |
Tomcat
TPS : 4.06
Netty
TPS : 661.13
2차 분석
큰 차이로 Netty가 압도적으로 TPS가 높은 모습을 보여줬다.
calculator API의 경우 상당한 양의 계산을 하여 블로킹과 논블로킹의 차이를 볼 수 있는 API인데
둘다 CPU 사용의 제한이 있지만 Tomcat은 하나의 요청당 스레드가 블로킹되어 상당한 차이의 결과를 확인 할 수 있었는데
응답 시간부터 TPS까지 Netty가 압도적인 모습이다.
이 API는 비관적락을 적용한 동시성 제어 테스트로
각 톰캣과 네티에서 비관적락을 걸고 부하 테스트를 했을때 얼마만큼의 차이가 존재하지는 확인하기 위한 API이다.
Tomcat
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT pl FROM PessimisticLock pl WHERE pl.id = :id")
Optional<PessimisticLock> findByIdWithPessimistic(Long id);
Netty
@Lock(LockMode.PESSIMISTIC_WRITE)
@Query("SELECT * FROM pessimistic_lock WHERE id = :id FOR UPDATE")
Mono<PessimisticLock> findByIdWithPessimistic(Long id);
각각 JPA의 @Query, R2DBC의 @Query로 서로 다른 어노테이션의 Query이다.
다음과 같이 간단하게 테이블이 구성되어 있다.
모든 요청은 id = 1에 요청을 보내고 딱 100명만 받을 수 있게 락을 걸었다.
1차 테스트와 2차 테스트 결과의 차이가 그리 크지 않아 3차로 기입
| 설정 | 값 |
|---|---|
| 스레드 그룹 | 10000 |
| 램프업 | 0 |
| 반복 | 10 |
Tomcat
TPS : 424.9
Netty
TPS : 778.93
비관적락이 걸려있는 상황이라 전체적으로 TPS가 많이 낮아졌지만
비동기처리로 진행된 Netty가 대략 2배 이하로 더 TPS가 높은 모습이 보였다.
응답속도에서는 Netty가 3 ~ 4배 가량 더 빠른 응답속도를 보였다.
비동기 + 통신에서 callback을 받아 더 많은 요청을 수용할 수 있는 R2DBC가 JPA보다 더 높은 성능을 보인것 같다.
이 API는 MySQL 테이블로부터 간단한 SELECT 문을 요청하여 응답받는 API이다.
Tomcat
public String get() {
JustText justText = justTextRepository.findById(1L).orElseThrow(() ->
new IllegalArgumentException("없음")
);
return justText.getJustText();
}
Netty
public Mono<String> get() {
return justTextRepository.findById(1L)
.switchIfEmpty(Mono.error(new IllegalArgumentException("없음")))
.map(JustText::getJustText);
}
DB와의 연결은 각각 Tomcat은 JPA, Netty는 R2DBC를 통해 통신하고 있다.
부하 테스트에서 1차(스레드 100, 램프업 0, 반복 수 10)에서는 큰 차이가 없어 다음과 같은 세팅에서 테스트했다.
| 설정 | 값 |
|---|---|
| 스레드 그룹 | 1000 |
| 램프업 | 0 |
| 반복 | 10 |
Tomcat
TPS : 1016.86
Netty
TPS : 1502.96
Netty가 1.5배 정도 높은 TPS가 나왔다.
톰캣은 블로킹 I/O특성으로 JPA의 과부하로 스레드가 제한되지만 그에 반해
네티는 논블로킹 I/O특성과 추가적으로 R2DBC특성으로 더 많은 요청을 처리하는 결과인 것 같다.
ping/pong API에서는 톰캣과 네티간의 차이가 그리 크지 않았고 calculator 처럼 연산이 많은 작업에서는 상당히 큰 격차로 네티가 톰캣에 비해 높은 TPS를 보여줬다.
하나의 요청마다 스레드가 연산에 블로킹되면서 성능이 큰 영향을 끼쳤다.
lock의 경우에는 비관적 락의 영향으로 둘다 성능제한이 크게 걸려 큰 차이를 보이지는 않은 것 같다.
그나마 스레드 전환으로 더 많은 요청을 받을 수 있는 네티가 TPS가 더 높게 나왔다.