spring

spring boot 가상 스레드로 Async 및 Scheduled 어노테이션 사용

kimbs0301 2024. 10. 12. 10:09

Spring Boot에 Java 21 LTS 가상 스레드 및 Async 및 Scheduled 어노테이션 사용 하기

 

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.5'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.example.vt'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '21'
}

configurations {
	all {
		exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
	}
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/release/' }
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-log4j2'
	implementation 'mysql:mysql-connector-java:8.0.33'
	compileOnly 'org.projectlombok:lombok:1.18.32'
	annotationProcessor 'org.projectlombok:lombok:1.18.32'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.withType(JavaCompile).configureEach {
	options.encoding = 'UTF-8'
}

tasks.named('jar') {
	enabled = false
}

tasks.named('test') {
	useJUnitPlatform()
}

 

application.yml

server:
  shutdown: graceful

spring:
  threads:
    virtual:
      enabled: true

  task:
    execution:
      thread-name-prefix: vt-task-
      pool:
        core-size: 1
    scheduling:
      thread-name-prefix: vt-scheduling-

 

ExecutorServiceConfig.java

package com.example.vt.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
import org.springframework.boot.autoconfigure.thread.Threading;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Configuration
public class ExecutorServiceConfig {

    @Bean
    @ConditionalOnThreading(Threading.VIRTUAL)
    public ExecutorService virtualThreadExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor();
    }
}

 

AsyncConfig.java

package com.example.vt.config;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;

import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;

@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Bean(name = "virtualAsyncTaskExecutor")
    public AsyncTaskExecutor virtualAsyncTaskExecutor(@Qualifier("virtualThreadExecutor") ExecutorService virtualThreadExecutor) {
        return new TaskExecutorAdapter(virtualThreadExecutor);
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }

    public static class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
            //log("Exception Name - {}", throwable.getClass().getName());
            //log("Exception message - {}", throwable.getMessage());
            //log("Method name - {}", method.getName());

            for (Object param : params) {
                //log("Parameter value - {}", param);
            }
        }
    }
}

 

ApiScheduler.java

package com.example.vt;

import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j2;
import java.util.concurrent.TimeUnit;

@Log4j2
@RequiredArgsConstructor
@Component
public class ApiScheduler {

    @Scheduled(initialDelay = 3L, fixedDelay = 3L, timeUnit = TimeUnit.SECONDS)
    public void scheduleFixedDelay() {
        log.info("scheduleFixedDelay");
    }

    @Scheduled(cron = "*/3 * * * * ?", zone = "UTC")
    public void scheduleCron() {
        log.info("scheduleCron");
    }
}

 

PushService.java

package com.example.vt.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class PushService {

    @Async("virtualAsyncTaskExecutor")
    public void push() {
        //log("push");
    }
}

 

Application.java

package com.example.vt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

 

스프링 부트에서 가상 스레드를 사용하기 위해 설정 파일에서 활성화 application.yml

spring:
  threads:
    virtual:
      enabled: true

 

간략한 스프링 부트 설정과 어노테이션 추가로 스케쥴러와 비동기 작업

@EnableScheduling, @Scheduled 어노테이션 추가로 스프링 부트에서 스케쥴러를 사용

@EnableAsync, @Async 어노테이션 추가로 스프링 부트에서 해당 메서드를 비동기 작업

 

 

플랫폼 스레드 보다 가상 스레드 사용으로 처리량을 높일 수 있다.

 

 

references
https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html
https://docs.spring.io/spring-framework/reference/integration/scheduling.html

 

'spring' 카테고리의 다른 글

spring boot custom auto configuration  (0) 2024.10.12
spring boot fast log4j2  (0) 2024.10.12
spring boot 가상 스레드  (0) 2024.10.12
spring boot 가상 스레드로 Redis 사용  (0) 2024.10.12
spring boot 가상 스레드로 웹소켓 사용  (0) 2024.10.08