태그
#SPRING
#KOTLIN
#SPRINGBOOT
#ASYNC
2023년 11월 30일 13:29

사용법 (Spring Boot)
spring boot에서 @Async를 사용하는 방법은 간단합니다
- applicationClass에 @EnableAsync를 선언합니다
@EnableAsync
@SpringBootApplication
public class SpringBootApplication {
...
}
- 비동기로 작동하길 원하는 로직 함수 위에 @Async annotation을 붙어줍니다
public class AsyncClass {
@Async
public void asyncMethod(String param) {
...
}
}
설정
spring은 기본값으로 SimpleAsyncTaskExecutor를 사용하여 실제 메소드들을 비동기로 실행합니다 설정은 두 가지 레벨로 오버라이드할 수 있습니다
- 메소드 레벨로 실행자 오버라이드 하기 설정 코드
@Configuration
@EnableAsync // 설정 클래스에 붙이기
public class SpringAsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
사용 코드 : 속성 값으로 사용합니다 (bean의 이름값)
@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
System.out.println("Execute method with configured executor - " + Thread.currentThread().getName());
}
- 어플리케이션 레벨로 실행자 오버라이드 하기 설정 코드 : AsyncConfigurer를 구현해줍니다
@Configuration
@EnableAsync // 설정 클래스에 붙이기
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
}
- 옵션
- ThreadPoolTaskExecutor
- setCorePoolSize : 코어 스레드가 시간 초과되도록 허용할지 여부를 지정합니다
- setMaxPoolSize : ThreadPoolExecutor의 최대 풀 크키글 설정합니다
- setQueueCapacity : ThreadPoolExecutor의 BlockingQueue(max pool size 초과 요청 시 요청을 queue에 저장하는데 이때 최대 수용가능한 queue의 수)에 대한 용량을 설정합니다
- initialize : 대상 ExecutorService 인스턴스를 만듭니다
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html
커스텀 설정이나, 풀을 사용할 때에는 application class에서 @EnableAsync를 제거합니다 런타임 시 @Configuration이 설정된 AsyncConfig 클래스를 읽어들이기 때문입니다
리턴 타입
- Future
- 메소드의 결과를 전달받아야 한다면, Future를 사용해야 합니다
- 스프링에서 제공하는 AsyncResult는 Future의 구현체 입니다
비동기 메소드
@Async
public Future<String> getFuture(String str) throws InterruptedException {
...
return new AsyncResult<>(str);
}
비동기 메소드 사용 코드
Future<String> future = service.getFuture("test");
future.get();
- future의 get 메소드는 결과를 조회할 때까지 계속 기다립니다
- 즉, 메서드의 수행이 완료될 때 까지 기다려야하며, 블록킹 현상이 발생합니다
- Listenablefuture
비동기 메소드
@Async
public ListenableFuture<String> getFuture(String str) throws InterruptedException {
...
return new AsyncResult<>(str);
}
비동기 메소드 사용 코드
Listenablefuture<String> future = service.getFuture("test");
future.addCallback(f -> log.info("{}", f));
- addCallback 메소드는 비동기 메소드의 내부 로직이 완료되면 수행되는 콜백 기능입니다
- CompletableFuture
비동기 메소드
@Async
public CompletableFuture<String> getFuture(String str) throws InterruptedException {
...
return new AsyncResult<>(str).completable();
}
비동기 메소드 사용 코드
CompletableFuture<String> future = service.getFuture("test");
future.thenAccept(f -> log.info("{}", f));
- thenAccept 메소드는 비동기 메소드의 결과를 기다리지 않고 다음 작업을 계속 수행할 수 있게 해줍니다
- 더 많은 함수를 사용할 수 있습니다
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
에러 처리
- 리턴 타입이 Future 타입인 경우
구현 코드 : AsyncUncaughtExceptionHandler를 상속받습니다
@Configuration
@EnableAsync
public class AsyncExceptionHandler implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
- 리턴 타입이 void인 경우
구현 코드 : 리턴 타입인 void인 경우 예회가 호출 스레드로 전파되지 않기 때문에 따로 예외처리를 해주어야합니다
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
...
}
}
주의사항
- private method는 사용이 불가합니다
- @Async의 동작은 AOP가 적용됩니다 (@Async가 적용된 method의 경우 스프링이 메소드를 가로채 다른 스레드에서 실행 시켜주는 동작 방식입니다)
- 떼문에 Spring이 @Async 메소드를 가로챈 후, 다른 클래스에서 호출이 가능해야하므로 privat 메소드는 사용할 수 없습니다
- self-invocation은 불가합니다 (inner method)
- 같은 객체 내의 메소드끼리 호출할 시 AOP가 동작하지 않기 때문에 비동기로 동작하지 않습니다.
읽어주셔서 감사합니다!
참고 사이트
https://spring.io/guides/gs/async-method/
https://steady-coding.tistory.com/611
https://velog.io/@gillog/Spring-Async-Annotation%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
https://brunch.co.kr/@springboot/401