개요 및 설명
최근 개발한 한 도메인 서비스를 이용해 로컬 환경에서의 Webflux와 비교 테스트를 진행해보았다.
테스트 도구는 JMeter를 활용했다. Gatling이 기능이 더 많고 리포트 ui도 좋아서 사용하려고 했는데 Webflux 구현을 스프링부트 3.대와 jdk 17을 사용하였더니 지원을 하지 않아서 JMeter로 했다. Cpu 수치에 대해서는 Spring actuator에서 제공해주는 메트릭을 Prometheus와 Grafana를 이용해 모니터링했다.
테스트는 총 3가지 부하 환경을 구성하여 진행하였다.
- 노멀한 상황으로 유저 5명 상황
- 극단적 상황으로 동시 유저 200명 상황
- 초 극단적 상황으로 동시 유저 10,000명 상황.
테스트의 목적은 논블로킹과 비동기 환경을 제공하는 webflux가 mvc보다 얼마나 더 빠른가를 알아보고자 하는 것이었다. 즉, 단순 궁금증 해소가 목적이었다.
테스트는 조회 요청에 대한 응답을 대상으로 한다. 데이터 플로우 자체는 최대한 동일하게 구성하였고 Webflux의 경우 Mysql R2dbc를 이용하여 Webflux 튜토리얼에서 제공하는 논블로킹, 비동기 상황을 최대한 재현하려고 했다. 다른 점은 MVC의 경우 클라우드 상의 mysql을 사용하는 반면, Webflux는 로컬 도커 환경의 Mysql을 사용한 부분이다. 이점을 감안하면 나머지 환경은 대부분 동일하도록 구성하였다.
테스트 환경
- 장비: macbook pro 13` 2020 (m1), 16GB, Ventura 13.5
- JMeter, Spring Actuator(Prometheus, Grafana)
- Mvc: Springboot 2.6.7, mysql 8.0 (IDC)
- Webflux: Springboot 3.1.5, mysql 8.0 (로컬 도커)
- db 데이터 : 더미 데이터 10,000건
- 요청: 페이징이 적용된 전체 조회 요청
먼저 MVC 테스트 결과를 살펴보자.
MVC 테스트
1. 유저 5명 테스트
- 루프 카운트: 10,000
- 실행시간: 05:59
- average: 35
- min: 0
- max: 534
- std. dev: 13.90
- error: 0.00%
- throughput: 139.2/sec
- cpu: 0.3~0.45 (system), 0.044~0.065(process)
2. 유저 200명 테스트
- 루프 카운트: 300
- 실행시간: 06:31
- average: 1299
- min: 0
- max: 2514
- std. dev: 125.97
- error: 0.00%
- throughput: 153.2/sec
- cpu: 0.35~0.44 (system), 0.055~0.066(process)
3. 유저 5,000명 테스트
- 루프 카운트: 5
- 실행시간: 02:07
- average: 16585
- min: 0
- max: 120182
- std. dev: 12239.41
- error: 1.19%
- throughput: 98.3/sec
- cpu: 0.2~0.50 (system), 0.001~0.070(process)
Webflux 테스트 결과는 다음과 같다.
Webflux 테스트
1. 유저 5명 테스트
- 루프 카운트: 10,000
- 실행시간: 00:32
- average: 3
- min: 0
- max: 162
- std. dev: 4.82
- error: 0.00%
- throughput: 1556.3/sec
- cpu: 0.4(system), 0.040~0.090(process)
2. 유저 200명 테스트
- 루프 카운트: 300
- 실행시간: 00:37
- average: 120
- min: 0
- max: 866
- std. dev: 51.98
- error: 0.00%
- throughput: 1617.3/sec
- cpu: 0.35 ~ 0.475 (system), 0.095~0.097(process)
3. 유저 3,000명 테스트
- 루프 카운트: 5
- 실행시간: 00:17
- average: 2445
- min: 0
- max: 3799
- std. dev: 580.96
- error: 0.05%
- throughput: 849.9/sec
- cpu: 0.25 (system), 0.2 ~ 0.9 (process)
4. 유저 10,000명 테스트
- 루프 카운트: 2
- 실행시간: 00:50
- average: 2184
- min: 0
- max: 6155
- std. dev: 1646.14
- error: 15.25%
- throughput: 852/sec
- cpu: 0.15 (system), 0.5 ~ 1 (process)
위의 테스트 결과를 비교해보면 다음과 같다.
비교 정리
항목/환경 | MVC | Webflux | 차이 |
유저 5명 테스트 | |||
루프 카운트 | 10,000 | 10,000 | 동일 |
실행시간 | 05:59 | 00:32 | 약 11배 빠름 |
평균 응답시간 | 35ms | 3ms | 약 11.6배 빠름 |
처리량 | 139.2/sec | 1556.3/sec | 약 11.2배 높음 |
오류율 | 0.00% | 0.00% | 동일 |
CPU (프로세스) | 0.044~0.065 | 0.040~0.090 | 비슷 |
항목/환경 | MVC | Webflux | 차이 |
항목/환경 | MVC | Webflux | 차이 |
유저 200명 테스트 | |||
루프 카운트 | 300 | 300 | 동일 |
실행시간 | 06:31 | 00:37 | 약 10.6배 빠름 |
평균 응답시간 | 1299ms | 120ms | 약 10.8배 빠름 |
처리량 | 153.2/sec | 1617.3/sec | 약 10.6배 높음 |
오류율 | 0.00% | 0.00% | 동일 |
CPU (프로세스) | 0.055~0.066 | 0.095~0.097 | 약 1.7배 높음 |
항목/환경 | MVC | Webflux | 차이 |
유저 3,000명 테스트 | |||
루프 카운트 | 5 | 5 | 동일 |
실행시간 | 02:07 | 00:17 | 약 7.4배 빠름 |
평균 응답시간 | 16585ms | 2445ms | 약 6.8배 빠름 |
처리량 | 98.3/sec | 849.9/sec | 약 8.6배 높음 |
오류율 | 1.19% | 0.05% | MVC가 높음 |
CPU (프로세스) | 0.001~0.070 | 0.2~0.9 | Webflux가 높음 |
결론
종합적으로 봤을 때, Webflux가 MVC보다 훨씬 높은 성능을 보여준다. 특히 응답시간과 처리량에서 압도적인 차이를 보이며, 동시 사용자 수가 높아질수록 그 차이가 더욱 명확해지는데 체감상 기대했던 것보다 훨씬 빨랐다.
처음 의도한 것은 초 극단적인 상황으로 유저를 10,000명 조건을 테스트해보는 것이었다. 그런데 MVC와 JMeter의 경우 4,000명 이상부터 프로그램이 응답을 안하는 등 테스트 자체가 어려워지는 상황이 발생했다. 그래서 3,000명을 최대치로 설정할 수 밖에 없었는 반면 Webflux는 10,000명까지도 거뜬히 커버하는 모습을 보여서 오히려 더 비교가 되는 부분이 아니었나 싶다. 유저 수가 극단적으로 많아지고 루프 수가 늘어날 수록 Webflux에서도 오류률은 증가했다.
한계점은 두 가지 정도 추려볼 수 있을 것 같습니다.
한계 1: db 환경이 동일하지 않다.
MVC의 경우 클라우드 db이고 Webflux의 경우 로컬의 도커 db이다. 둘 사이의 차이에서 오는 오차율을 감안해야 하는 점이 첫 번째 한계점이다. Webflux 사용시 CPU 사용량이 순간적으로 MVC보다 높게 나타나는 현상도 있는데 아마 로컬 도커를 사용하기 때문이 아닐까 추론해보았다. 이외에도 정확한 비교에 있어서 다른 조건이라 다소 아쉬운 부분이었다.
한계 2: JMeter의 성능이 부족하다.
JMeter의 성능이 테스트 조건을 따라오지 못하는 듯 한다. MVC의 경우 유저를 4,000명 이상 올렸을 때 루프 수를 2 이상 올리면 어느 시점에서 요청을 더 이상 보내지 못하는 상황이 발생했는데, MVC의 반응속도 문제도 있겠지만 이를 처리하는 JMeter에도 부하가 걸리는 듯 했다.
단순 궁금증 해소를 목적으로 진행해보았지만 성능 차이가 생각했던 것보다 컸다는 점에서 여러모로 놀라운 경험이었다. 생각했던 것보다 훨씬 빠른 성능을 보여주는 게 놀라웠다. 단, 보다 정확한 테스트를 위해서는 완전히 동일한 환경 구성과 보다 좋은 성능 테스트 도구를 사용해보아야 할 것 같다.
'Programming > Java, Spring' 카테고리의 다른 글
간단한 코드 한 줄로 NPE (Null Pointer Exception) 방지하는 방법 (0) | 2024.02.18 |
---|---|
스프링부트 junit 테스트 `No tests found for given includes:` 메시지 (0) | 2023.12.17 |
spring 단위 테스트에서 autowired와 mockbean의 차이 (feat. spybean) (0) | 2023.12.13 |
spring mock mvc에서 한 번쯤은 만나는 unnecessary stubbing을 lenient 옵션으로 해결하기 (0) | 2023.12.13 |
스프링 캐싱 메커니즘에서 @Cacheable과 @Cacheput 차이점은 ? (0) | 2023.12.13 |