Skip to content

Lab-pository/hello-virtual-thread

Repository files navigation

Hello Virtual Thread

Virtual Thread 란?

Java Virtual Thread

가상스레드를 사용하는 방법

Java21을 사용하면 스프링 부트 3.2부터 다음과 같이 application.yml 에 다음과 같은 설정을 추가해서 간단하게 가상 스레드를 사용할 수 있다.

spring:
  threads:
    virtual:
      enabled: true

다음과 같이 설정하고 http/product-api.http에서 상품을 생성하는 API를 호출했을 때 현재 어떤 스레드를 사용하는지 로그를 찍어보도록 하자.

적용 전

virtual-thread-disabled.png

  • 상품을 생성하는 스레드는 그냥 스레드에서 동작하고, 비동기적으로 ProductCreateEvent를 만드는 것도 AsyncConfig에서 지정한 스레드 풀의 스레드를 사용하는 것을 확인할 수 있다.

적용 후

virtual-thread-enabled.png

  • 상품을 생성하는 스레드 및 비동기적으로 ProductCreateEvent를 생성하는 스레드도 모두 가상 스레드를 사용하는 것을 확인할 수 있다.

가상 스레드를 적용하지 않은 경우, 가상스레드를 적용한 경우 각각 부하테스트

Environment

  • web container는 tomcat.server.threads.max=1로 설정하여, 최대 스레드가 1개로 서버가 동작되도록 설정한다.
  • Dockerfile 빌드 시 System Property로 -Djdk.virtualThreadScheduler.maxPoolSize=1과 같이 설정해준다.
    • 이를 통해 가상스레드 스케줄러가 최대 1개의 플랫폼 스레드를 사용하도록 설정한다.
  • 가상스레드를 적용한 경우와 적용하지 않은 경우 각각 부하테스트에서 50명의 가상 유저가 동시에 GET /health 요청을 보내도록 한다.
    • GET /health API 는 다음과 같이 구현돼 있다.
@GetMapping("/health")
fun healthCheck(): String {
    log.info("Thread: {}", Thread.currentThread())
    Thread.sleep(1000L) // Blocking
    return "OK"
}

가상 스레드를 적용하지 않은 경우

## Locust 폴더로 이동
cd locust/

## 테스트 실행: 아래 명령어를 실행 후 `localhost:8089`에서 테스트를 실행시킨다.
docker compose -f docker-compose-platform.yml up -d

## 종료 시
docker compose -f docker-compose-platform.yml down

부하테스트 실행 결과

not-using-virtual-thread-1.png

not-using-virtual-thread-2.png

  • 스레드가 1개이기 때문에, 50명의 유저가 요청을 보내도 1개만 응답이 가능하다. 따라서 RPS가 1인 것을 확인할 수 있다.
    • 1개의 요청이 처리되는 동안 나머지 응답은 대기하므로 응답시간이 계속 증가하는 것을 확인할 수 있다.

가상 스레드를 적용한 경우

## Locust 폴더로 이동
cd locust/

## 테스트 실행: 아래 명령어를 실행 후 `localhost:8089`에서 테스트를 실행시킨다.
docker compose -f docker-compose-virtual.yml up -d

## 종료 시
docker compose -f docker-compose-virtual.yml down

부하테스트 실행 결과 with VisualVM Monitoring

using-virtual-thread-1.png

using-virtual-thread-2.png

  • 사용하는 플랫폼 스레드는 1개이지만, 가상 스레드를 만들어서 응답을 하기 떄문에 RPS가 50인 것을 확인할 수 있다.
    • 50명의 유저가 있고, 응답하는데 1초가 걸리기 때문에 자연스럽다.
  • 가상스레드가 계속 생성되고 GC로 사라지기 때문에, Heap 영역의 메모리가 올라갔다 내려갔다 하는 것으로 추측된다.

Reference