Spring @Async
- 메소드를 비동기 처리하면 호출자는 호출한 메소드가 완료될 때 까지 기다릴 필요가 없다.
- @Async를 사용하여 별도의 쓰레드에서 실행시킬 것이다.
Async Configuration
// Java Config
@Configuration
@EnableAsync
public class SpringAsyncConfig {
...
}
// XML Config
<task:executor id="myexecutor" pool-size="5"/>
<task:annotation-driven executor="myexecutor"/>
@Async
제약사항
- @Async로 명시된 메소드는 반드시 public으로 선언되어야 한다. 메소드가 public 이어야 프록시가 될 수 있기 때문이다.
- 같은 클래스 내에서 해당 메소드를 호출할 경우 비동기로 작동하지 않는다. 셀프호출은 프록시를 우회하고 해당 메소드를 직접 호출하기 때문이다.
void 반환형
@Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
Future 반환형
@Async
public Future<String> asyncMethodWithReturnType() {
System.out.println("Execute method asynchronously - "
+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<String>("hello world !!!!");
} catch (InterruptedException e) {
//
}
return null;
}
Executor
- 스프링은 기본값으로 SimpleAsyncTaskExecutor를 사용하여 실제 메소드들을 비동기 실행한다. 기본 설정은 메소드/애플리케이션 레벨로 재정의하여 사용할 수 있다.
- 다음과 같이 설정하면 @Async로 지정된 메소드를 실행하는 기본 실행자가 변경된다. 실행자의 상세 옵션들도 변경할 수 있다.
Method Level Override
- 실행자 빈을 설정하고 @Async 속성에 실행자명을 명시해야 한다.
@Configuration
@EnableAsync
public class SpringAsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
System.out.println("Execute method with configured executor - " + Thread.currentThread().getName());
}
Application Level Override
- AsyncConfigurer 인터페이스를 구현하여 getAsyncExecutor() 메소드를 오버라이드해야 한다.
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("TEST_");
executor.initialize();
return executor;
}
}
Exception Handling
- 반환형이 void일 때, 예외는 호출 스레드에 전달되지 않는다. 예외 처리를 위한 설정이 추가적으로 필요하다.
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("TEST_");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
// Custom 가능
// return new CustomAsyncExceptionHandler();
}
}
- AsyncUncaughtExceptionHandler 인터페이스를 구현하여 커스텀 비동기 예외처리자를 만들 수도 있다. handleUncaughtException() 메소드는 잡히지 않은 비동기 예외가 발생할때 호출된다.
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception message : " + throwable.getMessage());
System.out.println("Method name : " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value : " + param);
}
}
}
Etc
Spring Boot 기반의 웹 애플리케이션은 HTTP 요청이 들어왔을 때 내장된 서블릿 컨테이너가 관리하는 독립적인 1개의 Worker 쓰레드에 의해 동기 방식으로 실행된다. 하지만 요청 처리 중 @Async가 명시된 메소드를 호출하면 앞서 등록한ThreadPoolTaskExecutor 빈에 의해 관리되는 또 다른 독립적인 Worker 쓰레드로실행된다. 별도의 쓰레드로 동작하기 때에 원래의 요청 쓰레드는 그대로 다음 문장을 실행하여 HTTP 응답을 마칠 수 있다.
Async 어노테이션을 사용하는 순간 서로 다른 Thread에서 동작하기 때문에 앞에서 생성한 Transaction이 연장되지 않으므로 주의해야 한다.
'Development > Spring' 카테고리의 다른 글
DatabaseConfiguration 적용 (0) | 2018.08.17 |
---|---|
Configuration Condition (0) | 2018.07.16 |
multiple 'X-Frame-Options' headers with conflicting 에러 (0) | 2017.12.15 |
비동기 메소드 HttpServletRequest 에러 (0) | 2017.12.03 |
캐시 어노테이션 (0) | 2017.10.06 |