WEB/Spring

[Spring] SpringBoot에서의 Virtual Thread

낭만주의 개발자 2023. 12. 31. 18:25

개요

Virtual Thread는 일반 Thread보다 가볍기 때문에 Context Switching비용을 효과적으로 줄일 수 있습니다.

따라서, Context Switching이 많이 발생하는 환경에서 이를 사용한다면 처리량을 늘릴 수 있습니다.

(참고 : Virtual Thread - https://nangmandeveloper.tistory.com/6)

 


SpringBoot에서 Virtual Thread사용하기

JAVA21을 사용하는 SpringBoot에서 Virtual Thread를 간단히 사용할 수 있습니다.

application.yml

application.yml에서 해당 설정을 추가하면 됩니다.

이렇게 되면, Tomcat 내부적으로 사용하는 Thread를 Virtual Thread로 교체할 수 있습니다.

일반 작업을 수행할 때 Virtual Thread를 사용하려면 위의 Virtual Thread글을 참고하시면 됩니다.

 

그렇다면, 이렇게 단순히 설정을 바꾸면 처리량을 늘릴 수 있을까요?


단순 HTTP Request 처리

 

Controller
Service

 

단순히 HTTP Request를 받으면 1, status 200을 반환하는 코드를 작성했습니다.

이후 Jmeter를 통해 부하 테스트를 진행해봤습니다.

 

virtual users : 2000

loop : 180

 

일반 Thread를 사용한 경우
Virtual Thread를 사용한 경우

 

 

위의 경우 Thread를 사용한 경우, 밑의 경우 Virtual Thread를 사용한 경우입니다.

단순 기능을 사용하는 경우 Virtual Thread의 성능이 오히려 떨어지는 것을 볼 수 있습니다.

동일한 양의 작업을 처리하는 데 Thread는 49초, Virtual Thread는 58초가 필요했습니다.

 

정확한 부분은 더 공부해봐야겠지만, 일반 Thread의 경우 Blocking이 발생하더라도 Context Switching이 발생하지 않는 데 비해 Virtual Thread는 잠깐의 Blocking에도 Context Switching이 발생해 생긴 문제가 아닐까 싶습니다.

 


Block 처리

그렇다면, Block이 발생한 경우 Virtual Thread를 사용하면 일반 Thread보다 좋은 결과가 나와야 할 것입니다.

 

Controller

 

Service

 

마찬가지로 간단하게 코드를 작성했습니다.

HTTP Request를 받으면 1초간 현재 Thread를 sleep상태에 빠지게 합니다.

이는 곧, Thread의 Blocked상태를 의미합니다.

 

이번에도 jmeter를 통해 테스트를 진행했습니다.

virtual users : 2000

loop : 30

 

일반 Thread를 사용한 경우

 

Virtual Thread를 사용한 경우

 

이번에는 유의미한 결과를 관측할 수 있었습니다.

일반 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 설정 변경

 

ThreadPool의 max thread를 1000으로 늘려 테스트를 진행했습니다.

 

일반 Thread, ThreadPool max 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로 변경한다면, 위의 테스트 결과처럼 자원을 더 효율적으로 사용할 수 있을 것입니다.

 

Example System Architecture

 

많은 변수들과 환경에 따라 결과가 달라질 것입니다.

위의 예시에서는, Longtime Request Distributor Instance에서 Virtual Thread로 작업을 진행하는 것이 효율적일 것입니다.