[Spring] SpringBoot에서의 Virtual Thread
개요
Virtual Thread는 일반 Thread보다 가볍기 때문에 Context Switching비용을 효과적으로 줄일 수 있습니다.
따라서, Context Switching이 많이 발생하는 환경에서 이를 사용한다면 처리량을 늘릴 수 있습니다.
(참고 : Virtual Thread - https://nangmandeveloper.tistory.com/6)
SpringBoot에서 Virtual Thread사용하기
JAVA21을 사용하는 SpringBoot에서 Virtual Thread를 간단히 사용할 수 있습니다.
application.yml에서 해당 설정을 추가하면 됩니다.
이렇게 되면, Tomcat 내부적으로 사용하는 Thread를 Virtual Thread로 교체할 수 있습니다.
일반 작업을 수행할 때 Virtual Thread를 사용하려면 위의 Virtual Thread글을 참고하시면 됩니다.
그렇다면, 이렇게 단순히 설정을 바꾸면 처리량을 늘릴 수 있을까요?
단순 HTTP Request 처리
단순히 HTTP Request를 받으면 1, status 200을 반환하는 코드를 작성했습니다.
이후 Jmeter를 통해 부하 테스트를 진행해봤습니다.
virtual users : 2000
loop : 180
위의 경우 Thread를 사용한 경우, 밑의 경우 Virtual Thread를 사용한 경우입니다.
단순 기능을 사용하는 경우 Virtual Thread의 성능이 오히려 떨어지는 것을 볼 수 있습니다.
동일한 양의 작업을 처리하는 데 Thread는 49초, Virtual Thread는 58초가 필요했습니다.
정확한 부분은 더 공부해봐야겠지만, 일반 Thread의 경우 Blocking이 발생하더라도 Context Switching이 발생하지 않는 데 비해 Virtual Thread는 잠깐의 Blocking에도 Context Switching이 발생해 생긴 문제가 아닐까 싶습니다.
Block 처리
그렇다면, Block이 발생한 경우 Virtual Thread를 사용하면 일반 Thread보다 좋은 결과가 나와야 할 것입니다.
마찬가지로 간단하게 코드를 작성했습니다.
HTTP Request를 받으면 1초간 현재 Thread를 sleep상태에 빠지게 합니다.
이는 곧, Thread의 Blocked상태를 의미합니다.
이번에도 jmeter를 통해 테스트를 진행했습니다.
virtual users : 2000
loop : 30
이번에는 유의미한 결과를 관측할 수 있었습니다.
일반 Thread를 사용한 경우 초당 약 200번의 처리량이 나타났고, 시간이 너무 오래 걸려 중단했습니다.
Virtual Thread를 사용한 경우 초당 2000번에 가까운 처리량이 보여졌습니다.
거의 10배에 가까운 처리량인 것입니다.
즉, Virtual Thread는 Blocked상태가 되면 바로 다음 Request를 처리하도록 하여 Request에 대해 빠른 반응 속도를 가지고 있습니다.
그렇다면, 일반 Thread는 왜 200번의 처리량이 한계였을까요?
해답은 Tomcat의 TheadPool에 있습니다.
(참고 : Spring의 ThreadPool - https://nangmandeveloper.tistory.com/10)
Tomcat은 Connection에 대해 ThreadPool을 사용하는데, max-thread의 default가 200입니다.
따라서, 한번에 200개의 작업에 대응할 수 있는 것입니다.
ThreadPool의 max thread를 1000으로 늘려 테스트를 진행했습니다.
ThreadPool의 max를 1000으로 늘리자, 초당 거의 1000에 가까운 Request가 처리되는 것을 볼 수 있습니다.
만약 ThreadPool의 max가 200인 채로 있었다면, 5분 넘게 걸릴 수 있었습니다.
결론
Virtual Thread는 이름만 봤을 때 Thread를 대체하는 것 같아 보이지만, 실상은 그렇지 않습니다.
Blocked 시간이 적은 작업을 진행하는 경우 일반 Thread를 사용하는 것이 처리량이 더 높습니다.
Blocked 시간이 큰 작업(IO작업, 많은 처리시간이 필요한 작업)을 수행하는 경우 Virtual Thread를 사용하면 효과적으로 Request에 대응할 수 있습니다.
다만, Monologue서버에서는 위처럼 간단히 Virtual Thread, Normal Thread를 전환하기 어렵습니다.
MSA를 이용하는 경우, blocked시간이 큰 작업을 수행하는 인스턴스, 컨테이너들에 대해 Virtual Thread를 사용하도록 하면 더욱 효과적으로 Virtual Thread를 사용할 수 있을 것입니다.
예를 들어, 사용자로부터 Request를 받고 다른 인스턴스의 API의 결과를 전달하는 인스턴스가 있습니다.
이때, 다른 인스턴스 API가 처리할 것이 많아 평균 소모시간이 1초가 걸린다 하면, 현재 인스턴스의 Thread는 1초 동안 Blocked상태가 될 것입니다.
이런 경우, 해당 인스턴스의 Thread를 Virtual Thread로 변경한다면, 위의 테스트 결과처럼 자원을 더 효율적으로 사용할 수 있을 것입니다.
많은 변수들과 환경에 따라 결과가 달라질 것입니다.
위의 예시에서는, Longtime Request Distributor Instance에서 Virtual Thread로 작업을 진행하는 것이 효율적일 것입니다.