Spring WebFlux, 코틀린 코루틴, 가상 스레드의 비동기 처리 방식과 성능 비교

궁금하면 500원·2024년 5월 29일

성능 비교 및 권장 사항

성능 비교

1. 비동기 처리 (WebClient 사용)

  • 장점: 안정적인 성능 유지, 부하가 커도 평균 응답 시간이 일정하게 유지됨.
@RestController
class UserController {
    private final WebClient webClient;

    public UserController(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("http://example.com").build();
    }

    @GetMapping("/user")
    public Mono<ResponseEntity<User>> getUser() {
        return webClient.get()
                        .uri("/user")
                        .retrieve()
                        .bodyToMono(User.class)
                        .map(ResponseEntity::ok);
    }
}

2. 블로킹 처리 (Blocking Code 사용)

  • 단점: 응답 시간이 급격히 증가하고 병렬성이 상실됨.
    블로킹 코드는 비동기 코드에 비해 성능을 크게 저하시킬 수 있음.
@RestController
class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/user")
    public ResponseEntity<User> getUser() {
        User user = userService.getUser(); // 블로킹 호출
        return ResponseEntity.ok(user);
    }
}

블로킹과 코루틴

1. 블로킹 호출을 코루틴과 함께 사용

  • 단점: 성능 저하 발생 가능.
    Context Dispatcher IO 사용 시 성능 개선 가능하지만, 스레드 풀의 한계에 도달할 수 있음.
@RestController
class UserController(private val userService: UserService) {
    @GetMapping("/user")
    suspend fun getUser(): ResponseEntity<User> {
        val user = userService.getUser() // Suspend function
        return ResponseEntity.ok(user)
    }
}

가상 스레드 (Virtual Threads) 사용

1.블로킹 서비스 호출을 가상 스레드와 함께 사용

  • 장점: 병렬 처리는 이루어지지 않지만 전체적인 응답 시간은 개선됨.
// Spring Boot 애플리케이션의 기본 설정 (application.properties)
spring.threads.virtual.enabled=true

@RestController
public class UserController {

    @GetMapping("/user")
    public CompletableFuture<ResponseEntity<User>> getUser() {
        return CompletableFuture.supplyAsync(() -> {
            User user = userService.getUser(); // I/O 바운드 작업
            return ResponseEntity.ok(user);
        });
    }
}

3.가상 스레드와 Context Virtual Thread Dispatcher 사용

  • 장점: 블로킹 작업을 효과적으로 처리하면서 성능 유지 가능.
@Configuration
public class VirtualThreadConfig {
    @Bean
    public Executor virtualThreadExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor();
    }
}

@RestController
class UserController {
    private final Executor executor;

    public UserController(Executor executor) {
        this.executor = executor;
    }

    @GetMapping("/user")
    public CompletableFuture<ResponseEntity<User>> getUser() {
        return CompletableFuture.supplyAsync(() -> {
            User user = userService.getUser(); // 블로킹 작업
            return ResponseEntity.ok(user);
        }, executor);
    }
}

권장 사항

1. 비동기 API

  • 권장 사항: 비동기 API만 사용하는 경우 가상 스레드보다 기존 플랫폼 스레드가 더 빠를 수 있음.

2.블로킹 코드가 있는 경우

  • 권장 사항: 블로킹 코드가 있는 애플리케이션에서는 가상 스레드 사용 고려.

3. 부하 테스트

  • 권장 사항: 성능 테스트를 통해 가상 스레드의 오버헤드와 성능 이점을 비교. 경우에 따라 Dispatcher IO와 같은 대안이 더 나을 수 있음.

결론

1. 높은 병렬성과 스트림 처리가 필요한 경우

  • 추천: Spring WebFlux와 코루틴을 사용하는 것이 좋음.

2. 병렬성 요구가 없는 경우

  • 추천: Spring Web과 가상 스레드를 사용하여 충분히 좋은 성능 유지 가능.

[참조]

Reactive Spring Boot With Kotlin Coroutines: Adding Virtual Threads

profile
에러가 나도 괜찮아 — 그건 내가 배우고 있다는 증거야.

0개의 댓글