스프링부트에서 annotation을 활용한 비동기 method 구현하기 - 두 번째
지난 포스팅에 이어,
이번 포스팅에서는 스프링부트에서 @Async Annotation을 사용할 때의 주의사항에 대해 확인해 보겠습니다.
1. private method는 사용 불가
@Async는 AOP가 적용되어 Spring Context에서 등록된 Bean Object의 method가 호출 될 때 Spring이 확인할 수 있고 @Async가 적용된 method의 경우 Spring이 method를 가로채 다른 Thread에서 실행 시켜주는 방식으로 동작합니다.
이 때문에 Spring이 해당 @Async method를 가로챈 후 다른 Class에서 호출이 가능해야 하므로, private method는 사용할 수 없는 것입니다.
2. self-invocation(자가 호출) 불가, 즉 inner method는 사용 불가
Spring Context에 등록된 Bean의 method의 호출이어야 Proxy 적용이 가능하므로, inner method의 호출은 Proxy 영향을 받지 않기에 self-invocation이 불가능합니다.
아래 샘플 코드는 동일 Class 내에 있는 method를 비동기 방식으로 사용하는 것입니다.
@Controller
public Class TestController {
@Async
public void asyncMethod(int i) {
try {
Thread.sleep(500);
log.info("[AsyncMethod]"+"-"+i);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
@GetMapping("async")
public String testAsync() {
log.info("TEST ASYNC");
for(int i=0; i<50; i++) {
asyncMethod(i);
}
return "";
}
}
이 샘플코드를 실행해 보면 method가 비동기 방식으로 호출되지 않습니다.
즉, 자가 호출에서는 @Async 사용이 불가능 하다는 의미입니다.
하지만 이 샘플 코드를 아래와 같이 바꿔보면 다릅니다.
@Service annotation을 사용하여 Bean 등록된 Service를 통해 주입하는 방식으로 바꾸어 본 것입니다.
@Service
public class TestService {
@Async
public void asyncMethod(int i) {
try {
Thread.sleep(500);
log.info("[epz-thread]"+"-"+i);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
@AllArgsConstructor
@Controller
public Class TestController {
private TestService testService;
@GetMapping("async")
public String testAsync() {
log.info("TEST ASYNC");
for(int i=0; i<50; i++) {
testService.asyncMethod(i);
}
return "";
}
}
이 샘플 코드를 실행해 보면 method 호출 순서와 상관없이 비동기 방식으로 호출 됩니다.
3. QueueCapacity 초과 요청에 대한 비동기 method 호출시 방어 코드 작성
AsyncConfig에서 PoolSize와 QueueCapacity 값을 줄여서, 위 샘플 코드를 실행해 보면 다른 결과가 나오게 됩니다.
TaskRejectedException이 발생하게 됩니다. 값을 줄인 PoolSize와 QueueCapacity 보다 초과된 요청에 의해서 발생하는 Exception 입니다. 따라서 이 Exception을 handling하는 방어 코드를 추가해 주어야 합니다.
@AllArgsConstructor
@Controller
public Class TestController {
private TestService testService;
@GetMapping("async")
public String testAsync() {
log.info("TEST ASYNC");
try {
for(int i=0; i<50; i++) {
testService.asyncMethod(i);
} catch (TaskRejectedException e) {
// ....
}
return "";
}
위와 같이, handling 코드를 작성해 주면 됩니다.
이상으로 스프링부트에서 비동기 method를 구현하는 방법에 대한 포스팅을 마무리 하겠습니다.
Reference
https://dzone.com/articles/effective-advice-on-spring-async-part-1
https://velog.io/@gillog/Spring-Async-Annotation%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0