티스토리 뷰
Spring boot - ThreadPoolTaskExecutor를 이용하여 비동기 처리(feat.이메일 발송)
realizers 2022. 4. 28. 15:56ThreadPoolTaskExecutor는 org.springframework.scheduling.concurrent 패키지에 속해있습니다.
ThreadPoolTaskExecutor를 사용한 예제 코드
로그가 출력된 시간을 보면 1초마다 출력이 되고있고 10번의 출력이 다 된것을 알 수 있습니다. 로그에서 볼 수 있듯이 아래 예제는 멀티 쓰레드로 돌아간게 아닌 하나의 쓰레드로 돌아간것을 알 수 있습니다. 그 이유는 ThreadPoolTaskExecutor는 기본적으로 동시에 실행할 쓰레드의 갯수가 1로 설정되어 있습니다.
public class Example {
public static void main(String[] args) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
Runnable thread = () -> {
try {
System.out.println(Thread.currentThread().getName() + ", Now sleeping 1 seconds...");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
};
for (int i = 0; i < 10; i++) {
executor.execute(thread);
}
}
}
ThreadPoolTaskExecutor의 옵션 값
💡setCorePoolSize (default : 1)
- 동시에 실행시킬 쓰레드의 갯수를 의미합니다.
💡setMaxPoolSize (default : Integer.MAX_VALUE)
- 쓰레드 풀의 최대 사이즈를 설정합니다.
💡setQueueCapacity (default : Integer.MAX_VALUE)
- 쓰레드 풀의 큐 사이즈
- corePoolSize의 개수를 넘어서는 task가 들어왔을 경우 queue에 해당 task들이 쌓이게 됩니다.
💡setKeepAliveSeconds (default : 60)
- maxPoolSize가 모두 사용되다가 idle로 돌아갔을 때 종료하기까지 대기하는데 걸리는 시간입니다.
- 예를 들어 CorePoolSize가 5이며, MaxPoolSize가 15라는 가정하에 요청이 많아져서 thread pool이 바빠져서 추가적으로 10개의 스레드를 생성해서 총 15개의 스레드를 사용하게 됩니다. 그리고 바쁜 타임지 지나 한가해지면 스레드는 idle 상태가 됩니다.
그리고 자원을 절약하기 위해 한가한 상태의 쓰레드는 죽게 됩니다. - 스레드가 idle 상태에서 죽게되는 상태가 되기까지 대기하는 시간을 setKeepAliveSeconds() 옵션으로 지정할 수 있습니다.
💡setWaitForTasksToCompleteOnShutdown (default : false)
- 시스템을 종료할 때 queue에 남아있는 작업을 모두 완료한 후에 종료하도록 처리합니다.
💡setAwaitTerminationSeconds (default : 0)
- 시스템을 종료할 때 queue에 남아있는 작업을 모두 완료한 후 종료하도록 처리하거나 타임아웃을 지정해 해당 시간이 넘으면 강제종료 시킵니다.
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 기본 스레드 수
taskExecutor.setMaxPoolSize(10); // 최대 스레드 수
taskExecutor.setQueueCapacity(100); // Queue 사이즈
return taskExecutor;
}
위와 같은 설정에서는 5개의 스레드로 처리하다가 처리 속도가 밀리는 경우 100개 사이즈 큐에 대기하고 있다가 그 보다 많은 요청이 발생하는 경우 최대 10개의 스레드까지 생성하여 처리하게 됩니다.
setCorePoolSize()를 3으로 설정
아래 코드가 실행되면 실행되자마자 3개의 쓰레드가 실행되고 10초 뒤에 3개의 쓰레드가 실행되는 것을 알 수 있습니다.
@Slf4j
public class Example {
public static void main(String[] args) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3); // 3으로 설정
executor.initialize();
Runnable thread = () -> {
try {
log.info(Thread.currentThread().getName() + ", Now sleeping 10 seconds...");
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
};
for (int i = 0; i < 10; i++) {
executor.execute(thread);
}
}
}
corePoolSize와 maxPoolsize, queueCapacity와의 관계
결과를 보면 queue의 사이즈만큼 내부의 queue에 1씩 증가하는 것을 알 수 있습니다. 그리고 queue안에서 max까지 증가했을 때 thread가 하나 더 active 되는 것을 확인할 수 있습니다. 따라서 core에서 max로의 thread 수의 증가는 queue의 사이즈가 가득 차게되면 추가로 thread를 생성하여 사용한다는 것을 알 수 있습니다.
@Slf4j
public class Example {
public static void main(String[] args) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setQueueCapacity(5);
executor.setMaxPoolSize(5);
executor.setThreadNamePrefix("async-");
executor.initialize();
Runnable thread = () -> {
try {
log.info(Thread.currentThread().getName() + ", Now sleeping 10 seconds...");
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
};
for (int i = 0; i < 10; i++) {
executor.execute(thread);
log.info("poolSize : " + executor.getPoolSize() +
", active : " + executor.getActiveCount() +
", queue : " + executor.getThreadPoolExecutor().getQueue().size());
}
}
}
이메일 발송을 비동기로 실행
https://kdg-is.tistory.com/316
AsyncConfig 클래스 작성
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("async-");
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
기존 메서드에 @Async 어노테이션 선언
@Component
@RequiredArgsConstructor
public class MailHandler {
private final JavaMailSender javaMailSender;
private final SpringTemplateEngine templateEngine;
/**
* 이메일 발송 함수
* @param title 이메일 제목
* @param to 받는 사람
* @param templateName 템플릿 이름
* @param values 이메일에 들어갈 값
* @throws MessagingException
* @throws IOException
*/
@Async
public void send(String title, String to, String templateName, HashMap<String, String> values) throws MessagingException, IOException {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setSubject(title); // 제목 설정
helper.setTo(to); // 수신자 설정
Context context = new Context();
values.forEach((key, value) -> {
context.setVariable(key, value);
});
String html = templateEngine.process(templateName, context);
helper.setText(html, true );
javaMailSender.send(message);
}
}
실행 시간 비교
5.54 ms => 11 ms 줄어든 것을 알 수 있습니다.
ThreadPoolTaskExecutor 예외 처리 방법
https://kdg-is.tistory.com/332
'JAVA > SpringBoot' 카테고리의 다른 글
Spring Boot - @Transactional 전파 옵션 및 예외 처리 (0) | 2022.06.27 |
---|---|
Spring boot - ThreadPoolTaskExecutor Exception Handling (0) | 2022.06.21 |
Spring boot - Thymeleaf 템플릿 메일발송 (0) | 2022.04.28 |
Spring boot - log4j2 를 사용하여 로그남기기 (0) | 2022.03.11 |
Spring boot - Spring Security에 대하여 (0) | 2022.02.22 |
- Total
- Today
- Yesterday
- redis sorted set으로 대기열 구현
- redis sorted set
- @ControllerAdvice
- spring boot excel download oom
- spring boot redisson 분산락 구현
- spring boot poi excel download
- JDK Dynamic Proxy와 CGLIB의 차이
- pipe and filter architecture
- microkernel architecture
- spring boot redisson sorted set
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- transactional outbox pattern spring boot
- 레이어드 아키텍처란
- 트랜잭셔널 아웃박스 패턴 스프링부트
- spring boot excel download paging
- java ThreadLocal
- spring boot redisson destributed lock
- space based architecture
- spring boot 엑셀 다운로드
- 공간 기반 아키텍처
- service based architecture
- 람다 표현식
- transactional outbox pattern
- java userThread와 DaemonThread
- polling publisher spring boot
- 서비스 기반 아키텍처
- redis 대기열 구현
- pipeline architecture
- spring boot redis 대기열 구현
- 자바 백엔드 개발자 추천 도서
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |