[spring] Async 설정 By starseat 2023-06-01 15:06:00 java/spring Post Tags Spring 으로 Async 설정 하는 방법은 [구글링(spring async)](https://www.google.com/search?q=spring+async&rlz=1C1YTUH_koKR1027KR1027&sxsrf=APwXEdd47MsJ3on5S6aAsDKchcnjJFFuaA%3A1685598198374&ei=9i94ZI-_FoT3-QbTpJKwBQ&ved=0ahUKEwiPkuTOrqH_AhWEe94KHVOSBFYQ4dUDCA8&uact=5&oq=spring+async&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIECCMQJzIFCAAQgAQyBQgAEIAEMgUIABCABDIFCAAQgAQyBQgAEIAEMgUIABCABDIFCAAQgAQyBQgAEIAEMgUIABCABDoECAAQR0oECEEYAFCVBliVBmDbCGgAcAJ4AIABYIgBYJIBATGYAQCgAQHAAQHIAQo&sclient=gws-wiz-serp) 으로 검색하면 나오니 자세한 사항은 생략하고 내가 설정한 방식만 기록하겠다. # 1. AsyncConfig 생성 ```java @EnableAsync @Configuration public class AsyncConfig extends AsyncConfigurerSupport { @Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(5); // 기본 쓰레드 사이즈 taskExecutor.setMaxPoolSize(20); // 최대 쓰레드 사이즈 taskExecutor.setQueueCapacity(10); // Max 쓰레드가 동작 하는 경우 대기 하는 queue 사이즈 taskExecutor.setThreadNamePrefix("TaskExecutor-"); taskExecutor.initialize(); return taskExecutor; } @Bean(name = "testTaskExecutor") public Executor testkTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(2); taskExecutor.setMaxPoolSize(4); taskExecutor.setQueueCapacity(1); taskExecutor.setThreadNamePrefix("TaskExecutor-test-"); taskExecutor.initialize(); return taskExecutor; } } ``` # 2. Async 적용 - 사용하고자 하는 부분에 @Async 를 붙여서 사용한다. - `private` 사용 불가 - `void` 형 method 에 사용 - etc... ```java @Slf4j @Service @RequiredArgsConstructor @Transactional public class TestService { @Async("testTaskExecutor") public void testAsync() { // todo... } } ``` # 3. Async 사용 - 예제로 Controller 에서 사용하였다. - `QueueCapacity` 초과 했을 경우에 대한 로직을 추가한다. ```java @Slf4j @RequiredArgsConstructor @RestController public class TestController { private final TestService service; @GetMapping("async-test") public String asyncTest() { // async 처리 try { service.testAsync(); } catch (TaskRejectedException e) { // QueueCapacity 초과 log.error("async Exception: ", e); } } } ``` --- # 추가 만약 `Unexpected exception occurred invoking async method` 라는 에러가 발생하면 다음과 같이 수정한다. ## build.gradle - `dependencies` 에 다음 항목 추가 ```yaml dependencies { // ... // spring boot 일 경우 implementation 'org.springframework.boot:spring-boot-starter-security' // spring 일 경우 (spring boot X) // implementation 'org.springframework.security:spring-security-config:6.1.1' } ``` ## AsyncConfig - 추가된 부분은 `return` 값 변경과 `@Bean` 추가 이다. ```java @Bean(name = "testTaskExecutor") public Executor testkTaskExecutor() { // ... // return taskExecutor; return new DelegatingSecurityContextAsyncTaskExecutor(taskExecutor) { public void shutdown() { taskExecutor.destroy(); } }; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { // return new ItacaExceptionHandler(); return new SimpleAsyncUncaughtExceptionHandler(); } @Bean public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(ThreadPoolTaskExecutor delegate) { return new DelegatingSecurityContextAsyncTaskExecutor(delegate); } ``` - 전체 소스 ```java @EnableAsync @Configuration public class AsyncConfig extends AsyncConfigurerSupport { @Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(5); // 기본 쓰레드 사이즈 taskExecutor.setMaxPoolSize(20); // 최대 쓰레드 사이즈 taskExecutor.setQueueCapacity(10); // Max 쓰레드가 동작 하는 경우 대기 하는 queue 사이즈 taskExecutor.setThreadNamePrefix("TaskExecutor-"); taskExecutor.initialize(); return taskExecutor; } @Bean(name = "testTaskExecutor") public Executor testkTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(2); taskExecutor.setMaxPoolSize(4); taskExecutor.setQueueCapacity(1); taskExecutor.setThreadNamePrefix("TaskExecutor-test-"); taskExecutor.initialize(); // return taskExecutor; return new DelegatingSecurityContextAsyncTaskExecutor(taskExecutor) { public void shutdown() { taskExecutor.destroy(); } }; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { // return new ItacaExceptionHandler(); return new SimpleAsyncUncaughtExceptionHandler(); } @Bean public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(ThreadPoolTaskExecutor delegate) { return new DelegatingSecurityContextAsyncTaskExecutor(delegate); } } ``` ## 추가 항목 출처 - [Spring Security Context](https://www.baeldung.com/spring-security-async-principal-propagation) - [DelegatingSecurityContextAsyncTaskExecutor causes potential memory leak #6837](https://github.com/spring-projects/spring-security/issues/6837) - [@Async를 사용한 스프링 Security 컨텍스트 전파](https://recordsoflife.tistory.com/571) Previous Post [java] 메모리 측정 Next Post [spring] 대량 데이터 및 트래픽 처리 경험담...