스프링부트(SpringBoot) 순차적 방식과 비동기처리(asyn애노테이션), Completablefuture와 성능 비교
배경
리얼환경에서의 서비스를 위해 실제 환경(l4 - API서버 3)에서 어느정도의 트래픽을 안전하게 처리할 수 있는지에 대한 산정 필요(서버 도입 시점 등)'
해당 테스트는 단순 select로직을 통한 테스트로 참고 부탁드립니다.
메서드 내에서 여러 곳으로부터의 데이터를 받아와 처리하는 작업이 있는 경우 async애노테이션, CompletableFuture를 사용한 방식이 훨씬이
성능상 훨씬 많은 이점을 가져다 줄 수 있습니다.
테스트 로직
기존로직
변경로직 (@Asyn 애노테이션과 CompletableFuture를 통한 비동기 처리)
쿼리 (위 : 변경쿼리, 아래 : 기존쿼리)
성능측정
L4장비에 트래픽 부하(gatling) - 부하 툴로는 gatling을 사용하였습니다. 무료로 사용할 수 있는 툴로 쉽게 사용하실 수 있습니다.
1. Request : 9000, Duration 10초 - TPS 300
기존 로직
변경로직
2. Request : 24000, Duration 10초 - TPS 800
기존로직 변경로직(ThreadPool - 200) 변경로직(ThreadPool - 400)
ThreadPool설정값 조정
스프링부트를 사용할 경우 별도 application.properties에 server.tomcat.max-threads 다음설정을 해주지 않는 경우
기본 max-thread의 개수는 200개가 된다. (The default value for max-threads is 200)
따라서 400으로 올려서 테스트 진행
스레드의 개수를 600으로 두고 테스트를 했을 떄는 다음과 같은 에러가 발생했다.
[java.util.concurrent.ThreadPoolExecutor@18da28a1[Running, pool size = 741, active threads = 741, queued tasks = 200, completed tasks = 207]] did not accept task: java.util.concurrent.CompletableFuture$AsyncSupply@47ff8bc8] with root cause
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.CompletableFuture$AsyncSupply@47ff8bc8 rejected from java.util.concurrent.ThreadPoolExecutor@18da28a1[Running, pool size = 741, active threads = 741, queued tasks = 200, completed tasks = 207]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
3. 결론
1번 테스트(TPS 300) 결과를 보면 Asyn annotation과 Completablefuture를 사용한 쪽이 응답 속도가 훨씬 안정적으로 처리되는 것을 볼 수 있다.(99th percentile 기준)
2번 테스트 결과(TPS 800)를 봤을 때 1/3 정도의 요청이 fail나는 것을 확인 할 수 있었고 TPS 800 은 요청에 대한 fail비율과 응답속도(100ms 이내를 기준으로 한다)로 보았을 때 서버 3대로 처리할 수 있는 요청량이 아니라는 판단을 할 수 있다.
그 이후 TPS 400, 500, 600 700으로도 테스트 한 결과 안정적(최대 50ms - 광고 비지니스 특성상)이내로 ADX, DSP에 값을 전달하기 위해서는
최대 TPS 300을 기준으로 운영방침을 가져갈 수 있을 것 같다.
TPS 300 -> 한 서버당 하루요청량 2500만을 기준으로 둘 수 있겠다.
(물론 카산드라 3.0에 데이터가 유입량과 로직 추가에 따른 부분을 항상 염두해 두어야 할 것이다.)
추가적으로, 비동기방식으로 처리를 했을 때 메모리, cpu사용률이 더 높은 것을 확인 할 수 있었다.
무작정 비동기방식이 좋다고 말할 수도 동기 방식이 좋다고도 말하기 힘들 것 같다.
단순한 작업은 동기 방식 한 메서드 내에서 순차적으로 이루어지지 않아도 되는 작업이 여러 개 있는 경우(DB호출, 외부 API호출 등)이 있는 경우에는
비동기 방식이 성능상 이점을 가져다 줄 수 있을 것이다.