이전 글에서는 CompleteFuture에 관한 비동기 처리를 작성하였습니다.
이번 글에서는 @Async를 통한 비동기 처리에서 알아보겠습니다.
@Async 어노테이션은 Spring Framework에서 제공하는 기능으로, 메소드를 비동기적으로 실행할 수 있게 해줍니다. 이 어노테이션이 적용된 메소드는 호출 시 별도의 스레드에서 실행되며, 호출자는 메소드의 실행 완료를 기다리지 않고 즉시 다음 작업을 진행할 수 있습니다.
비동기 처리의 주요 이점
응답성 향상: 시간이 오래 걸리는 작업을 백그라운드로 넘겨 사용자 인터페이스의 응답성을 유지할 수 있습니다.
처리량 증가: 여러 작업을 병렬로 처리하여 전체 시스템 처리량을 향상시킬 수 있습니다.
리소스 활용도 개선: CPU 및 I/O 작업을 더 효율적으로 활용할 수 있습니다.
Spring Boot에서 @Async를 사용하려면 몇 가지 설정이 필요합니다.
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("MyAsync-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
@Async 어노테이션을 사용하여 비동기 메소드를 구현하는 방법을 알아보겠습니다.
기본 비동기 메소드
@Service
public class EmailService {
@Async
public void sendNotificationEmail(String email) {
// 이메일 전송 로직 (시간이 오래 걸릴 수 있는 작업)
System.out.println("Sending email to: " + email);
try {
// 이메일 전송 시뮬레이션
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Email sent to: " + email + " in thread: " + Thread.currentThread().getName());
}
}
위 메소드를 호출하면 즉시 제어권이 반환되고, 이메일 전송은 백그라운드에서 계속 진행됩니다.
여러 스레드 풀 사용하기
여러 유형의 비동기 작업에 대해 서로 다른 스레드 풀을 사용할 수 있습니다.
Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "emailExecutor")
public Executor emailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("Email-");
executor.initialize();
return executor;
}
@Bean(name = "reportExecutor")
public Executor reportExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(20);
executor.setThreadNamePrefix("Report-");
executor.initialize();
return executor;
}
}
@Service
public class AsyncService {
@Async("emailExecutor")
public void processEmails() {
// 이메일 처리 로직
}
@Async("reportExecutor")
public void generateReports() {
// 보고서 생성 로직
}
}