Backend/Spring+Boot

@Async 설정

findmypiece 2021. 7. 13. 00:49
728x90

Spring 환경에서는 @Async 어노테이션으로 간단한게 논블로킹 로직을 구현할 수 있다. 결국 스레드 풀을 사용하는 것인데 기본 설정으로 사용하면 풀 사이즈는 1로 고정되어 1개만 동시실행이 가능하고 나머지 요청은 무한대로 큐에 쌓여서 대기하게 된다.

 

@Async 자체가 논블로킹을 지원하기 때문에 큐에만 쌓고 대기 바로 작업을 종료하지만 우리가 원하는 동작은 아니다. 스레드 풀 사용하는 경우 대부분 일정수치 이상 동시처리가 진행되길 원한다.

 

이것 말고도 경우에 따라 반드시 지정해야할 논블로킹 로직의 설정이 필요하기 때문에 일반적으로 아래와 같이 스레드풀을 커스텀하게 정의해서 사용한다.

@Bean
public Executor defaultAsyncExecutor() {
	ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
	taskExecutor.setCorePoolSize(10);
	taskExecutor.setMaxPoolSize(30);
	taskExecutor.setQueueCapacity(1000);
	taskExecutor.setThreadNamePrefix("DefaultAsyncExecutor-");
//		taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
	taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
	taskExecutor.setAwaitTerminationSeconds(10);
//		taskExecutor.setKeepAliveSeconds(60); //디폴트가 60임
//		taskExecutor.initialize();
	
	return taskExecutor;
}

실제 처리과정을 예로 들며 하나씩 설정을 살펴보자.

 

1. 최초 스레드풀에 setCorePoolSize 만큼의 스레드를 만들어 놓고 이게 모두 사용 중일 경우 setQueueCapacity 사이즈의 큐에 작업을 쌓아놓는다. 생성되는 스레드는 setThreadNamePrefix 라는 접두어를 가진다.

 

2. 그런데 setQueueCapacity 사이즈의 큐 마저도 가득 찬 상태라면 최대 setMaxPoolSize 만큼 스레드를 추가로 생성해서 동시처리량을 늘린다.

 

3. 스레드가 setAwaitTerminationSeconds 까지 끝나지 않으면 강제로 종료한다.

 

4. 2번에서 추가로 생성된 스레드는 setKeepAliveSeconds 까지 유휴 상태가 유지되면 풀에서 삭제된다. 기본값이 60초라 적당할 것 같아 굳이 지정하진 않았다.

 

5. setWaitForTasksToCompleteOnShutdown 설정에 의해 프로세스를 종료할 경우 큐에서 대기하던 작업이 모두 완료될 때까지 종료는 보류된다.

 

6. 마지막에 initialize() 는 반드시 해줘야 실제로 Executor를 사용할 수 있다고 공식문서에도 그렇게 나와있지만 하지 않더라도 Bean으로 등록될 때 initialize 한다.

 

7. 스레드를 setMaxPoolSize 까지 생성하고 setQueueCapacity 도 꽉 찬 상태라면 일반적으로 예외가 발생하는데 setRejectedExecutionHandler 를 통해 그 외 다른 선택지를 지정할 수 있다.

 

  • AbortPolicy: 기본 설정. RejectedExecutionException을 발생.
  • DiscardOldestPolicy: 오래된 작업을 skip.
  • DiscardPolicy: 처리하려는 작업을 skip.
  • CallerRunsPolicy: shutdown 상태가 아니라면 ThreadPoolTaskExecutor에 요청한 thread에서 직접 처리

 

그런데 내가 볼 때는 기본설정 말고는 그다지 메리트가 없어서 굳이 지정하지 않았다. 기존 작업을 skip하는 것은 좋은 선택이 아닌 것 같고 본래 스레드에서 처리하는 것 역시 논블로킹 작업을 해놓고 그에 따른 책임을 본 스레드에서 지는 셈이라 적절하지 않아 보인다.

728x90

'Backend > Spring+Boot' 카테고리의 다른 글

UriComponentsBuilder  (0) 2021.07.19
@RunWith(SpringRunner.class)  (0) 2021.07.13
CircuitBreaker, Resilience4j, RestClient  (0) 2021.07.09
ObjectMapper json 직렬화/역직렬화 주의사항  (0) 2021.07.07
@RequestMapping consumes, produces  (0) 2021.07.07