[Spring] @Async ๋น„๋™๊ธฐ ๋™์ž‘ ์›๋ฆฌ ํŒŒํ—ค์น˜๊ธฐ

2023. 12. 21. 22:19ใ†๐ŸŒฟ Spring

๋ชฉ์ฐจ

- ์„œ๋ก 

- @EnableAsync ๋™์ž‘์›๋ฆฌ

- @Async ๋™์ž‘์›๋ฆฌ

- ๊ฒฐ๋ก 

 

์‹ค์Šต ํ™˜๊ฒฝ

- Java 17

- Springboot 3.2.0

 

์„œ๋ก 

์‹ค๋ฌด์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด CompletableFuture ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์ง€๋งŒ ๋ฌธ๋“ @Async ์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•˜๋ฉด ๋” ํŽธ๋ฆฌํ•˜์ง€ ์•Š์„๊นŒ ํ•˜๋Š” ์ƒ๊ฐ๊ณผ @Async๋Š” ์–ด๋–ค ์›๋ฆฌ๋กœ ๋น„๋™๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•ด์กŒ๋‹ค. @Async ๋‚ด๋ถ€ ๋™์ž‘์„ ๋””๋ฒ„๊น… ํ•œ ๊ฒฐ๊ณผ ๊ฒฐ๊ตญ @Async ์—ญ์‹œ CompletableFuture์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” ์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ @Async ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€์™€ ๋”๋ถˆ์–ด @EnableAsync ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ๋‹ค๋ค„๋ณด๊ฒ ๋‹ค.

 

 

@EnableAsync ๋™์ž‘์›๋ฆฌ

@EnableAsync 

main() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค์— @EnableAsync ๋ฅผ ๋ถ™์ธ ํ›„ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ–ˆ๋‹ค.

๋””๋ฒ„๊ฑฐ๋ฅผ ์ฐ์œผ๋ฉด์„œ ์–ด๋–ค ์›๋ฆฌ๋กœ @Async ๊ฐ€ ๋“ฑ๋ก๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž. 

 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
    ...
	AdviceMode mode() default AdviceMode.PROXY;
}

 

@EnableAsync ๋‚ด๋ถ€์—์„œ AsyncConfigurationSelector ํด๋ž˜์Šค๋ฅผ ์ž„ํฌํŠธ ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

AdviceMode๋Š” PROXY ์™€ ASPECTJ 2๊ฐ€์ง€ ํƒ€์ž…์ด ์กด์žฌํ•˜๋Š”๋ฐ ์—ฌ๊ธฐ์„  PROXY๋กœ ๋””ํดํŠธ ์„ค์ •๋œ๋‹ค.

PROXY ํƒ€์ž…์€ JDK proxy-based ๋กœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์—๋งŒ ์ ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

 

AsyncConfigurationSelector

AsyncConfigurationSelector์—์„œ๋Š” AdviceMode์— ๋”ฐ๋ผ ProxyAsyncConfiguration์ด๋‚˜ AspectJAsyncConfiguration์„ ๋ฐฐ์—ด์— ๋‹ด์•„ ๋ฆฌํ„ดํ•œ๋‹ค. ์—ฌ๊ธฐ์„  PROXY ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ProxyAsyncConfiguration ๊ฐ€ ๋ฆฌํ„ด๋œ๋‹ค. ProxyAsyncConfiguration ํด๋ž˜์Šค๋กœ ์ด๋™ํ•ด๋ณด์ž.

 

 

 

ProxyAsyncConfiguration

์ด๊ณณ์—์„œ AsyncAnnotationBeanPostProcessor์„ Bean์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.

 

 

 

 

AsyncAnnotationBeanPostProcessor

ํ•ด๋‹น ๋นˆํ›„์ฒ˜๋ฆฌ๊ธฐ์—์„  AsyncAnnotationAdvisor๋ฅผ ์–ด๋“œ๋ฐ”์ด์ €๋กœ ํ• ๋‹นํ•œ๋‹ค.

(AsyncAnnotationAdvisor๋Š” BeanFactoryAware์˜ ๊ตฌํ˜„์ฒด๋กœ setBeanFactory()๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ)

AsyncAnnotationAdvisor๋กœ ์ด๋™ํ•ด ๋ณด์ž.

 

 

 

 

AsyncAnnotationAdvisor

AsyncAnnotationAdvisor์˜ ์ƒ์„ฑ์ž ๋ถ€๋ถ„์ด๋‹ค.

๋ณต์žกํ•ด ๋ณด์ด์ง€๋งŒ ๊ฒฐ๊ตญ Advisor์—์„   advice์™€ pointcut์„ ํ• ๋‹นํ•˜๋Š” ๋ถ€๋ถ„์ด ํ•ต์‹ฌ์ด๋‹ค.

๋งˆ์ง€๋ง‰ ๋‘ ๋ผ์ธ์—์„œ ๊ฐ๊ฐ buildAdvice()์™€ buildPointcut() ๋ฉ”์„œ๋“œ๋กœ ๊ฐ’์„ ํ• ๋‹นํ•˜๊ณ  ์žˆ๋‹ค.

 

 

์ค‘๊ฐ„ ์ •๋ฆฌ๋ฅผ ํ•˜์ž๋ฉด ์šฐ๋ฆฌ๋Š” @EnableAsync์˜ ๋™์ž‘๊ณผ์ •์„ ํŒŒ์•…ํ•˜๋Š” ์ค‘์ด๋ฉฐ @EnableAsync๋Š” @Async ๊ฐ€ ๋™์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ์‚ฌ์ „ ์„ค์ •๊ณผ์ •์œผ๋กœ ์•Œ๊ณ  ์žˆ๋‹ค. ์ฆ‰ @Async ๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์Šคํ”„๋ง์˜ ์‚ฌ์ „ ์„ค์ •๊ณผ์ •์ด๋‹ค. ์ง€๊ธˆ๊นŒ์ง€์˜ ๊ณผ์ •์„ ์‚ดํŽด๋ณด๋ฉด @Async ๊ฐ€ ๋™์ž‘ํ•˜๊ธฐ ์œ„ํ•ด์„  Proxy ์„ค์ •๊ณผ AsyncAnnotationAdvisor ์ด ํ•„์š”ํ•œ ๊ฑธ ์•Œ๊ฒŒ ๋๋‹ค.

 

๋‹ค์‹œ AsyncAnnotationAdvisor๋กœ ๋Œ์•„์™€ ํ•ด๋‹น ์–ด๋“œ๋ฐ”์ด์ €์—” ์–ด๋–ค ํฌ์ธํŠธ์ปท๊ณผ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ํ• ๋‹น๋˜๋Š”์ง€ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด ๋ณด์ž.

 

 

 

AsyncAnnotationAdvisor.buildAdvice()

advice๋Š”AnnotationAsyncExecutionInterceptor ๊ฐ€ ํ• ๋‹น๋œ๋‹ค.

 

 

 

 

AsyncAnnotationAdvisor.pointcut()

pointcut ์—๋Š” [..., interface org.springframework.scheduling.annotation.Async, ...] ์ด ํ• ๋‹น๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ AsyncAnnotationBeanPostProcessor๋ผ๋Š” ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ์— ์˜ํ•ด @Async๋Š” AsyncAnnotationAdvisor ๊ฐ€ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ํฌ์ธํŠธ์ปท์ด ๋œ๋‹ค.

 

 

๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ํ™•์ธํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

 

 

 

@Async ๋™์ž‘ ๊ณผ์ •

EnableAsync ๊ฐ€ ์–ด๋–ค ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํŒŒ์•…ํ–ˆ์œผ๋‹ˆ ์‹ค์ œ๋กœ @Async๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์ž. ์šฐ์„  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค์— @EnableAsync๋ฅผ ์„ค์ •ํ–ˆ๋‹ค. ์ด๋กœ์จ ์Šคํ”„๋ง๋ถ€ํŠธ๊ฐ€ ์‹คํ–‰๋  ๋•ŒAsyncAnnotationAdvisor ๊ฐ€ ๋“ฑ๋ก๋˜๋Š” ๊ฒƒ์„ ์œ„ ๊ณผ์ •์„ ํ†ตํ•ด ํ™•์ธํ–ˆ๋‹ค.

 

 

 

AsyncExecutionInterceptor

@Async ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด AsyncExecutionInterceptor.invoke()์—์„œ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑˆ๋‹ค.

์œ„ ๋‹ค์ด์–ด๊ทธ๋žจ์—์„œ AsyncAnnotationAdvice์— ์˜ํ•ด AnnotationAsyncExecutionInterceptor ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ๊ณผ AsyncExecutionInterceptor ์€ AnnotationAsyncExecutionInterceptor์˜ ๋ถ€๋ชจํด๋ž˜์Šค๋ž€ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๋งˆ์ง€๋ง‰ ๋ผ์ธ์—์„œ doSubmit()๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉฐ, ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” AsyncExecutionAspectSupport ์ถ”์ƒ ํด๋ž˜์Šค์— ์ •์˜๋ผ ์žˆ๋‹ค.

์ด๋•Œ ์ธ์ž๋กœ task ๋žŒ๋‹ค์‹๊ณผ executor๋ฅผ ๋„˜๊ฒจ ํ˜ธ์ถœํ•œ๋‹ค.

 

 

 

 

doSubmit

๊ฒฐ๊ตญ @Async ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ๋Š” ์ตœ์ข…์ ์œผ๋กœ doSubmit() ๋กœ์ง์— ๋”ฐ๋ผ ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•œ๋‹ค. ์ด๋•Œ @Async ๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ด ํƒ€์ž…์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ์ƒ์ดํ•˜๊ฒŒ ๊ตฌํ˜„๋œ๋‹ค.

์ด ๋ฆฌํ„ด ํƒ€์ž…์€ doSubmit()์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋กœ invoke() ๋ฉ”์„œ๋“œ์—์„œ invocation.getMethod().getReturnType()์— ํ•ด๋‹น๋œ๋‹ค.

 

 

๊ฐ๊ฐ์˜ ์ผ€์ด์Šค๋Š” 4๊ฐ€์ง€๋กœ ์ •๋ฆฌ๋œ๋‹ค.

1. CompletableFuture

2. ListenableFuture

3. Future

4. void

 

CompletableFuture์™€ ListenableFuture๋Š” ๋ชจ๋‘ Future ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

 

 

 

์‹ค์Šต ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด @Async ์–ด๋…ธํ…Œ์ด์…˜์ด ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ๋Š” ์–ด๋–ค ์‹์œผ๋กœ doSubmit() ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์ž.

@RestController
@RequiredArgsConstructor
public class SampleController {

    private final AsyncService asyncService;

    @GetMapping("return-async-future")
    public void returnFuture() throws InterruptedException, ExecutionException {
        log.info("before Async");
        asyncService.completableFutureAsync()
            .thenAccept(result -> log.info("Return of Async | {}", result));
        log.info("after Async");
        return;
    }

}



@Service
public class AsyncService {

    @Async
    public CompletableFuture<Integer> completableFutureAsync() throws InterruptedException {
        log.info("middle of completableFutureAsync()");
        return new AsyncResult<>(1).completable();
    }
}

 

 

์˜ˆ์‹œ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. 

@Async ์ฒ˜๋ฆฌ๋œ completableFutureAsync() ๋‚ด๋ถ€์˜ ๋กœ๊ทธ๊ฐ€ 'after Async' ๋ณด๋‹ค ๋Šฆ๊ฒŒ ์ถœ๋ ฅ๋˜๋Š”๋ฐ, ์ด๋Š” ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ๋…ผ๋ธ”๋กœํ‚น ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๋์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋Š” callback ํ˜•์‹์œผ๋กœ thenAccept ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌ๋œ๋‹ค. ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ด ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด thenAccep(), thenApply() ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

ํ˜„์žฌ ์ผ€์ด์Šค์—์„œ @Async ๋‹ฌ๋ฆฐ ๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ดํƒ€์ž…์€ CompletableFuture๋กœ ์ฒซ ๋ฒˆ์งธ ๋ถ„๊ธฐ๋ฌธ์— ๊ฑธ๋ ค ๋ฆฌํ„ด๋œ๋‹ค.

ํ•ด๋‹น ๋ถ„๊ธฐ๋ฌธ์—์„œ task๋ฅผ  submitCompletable() ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ ๋„˜๊ธฐ๊ณ  ์ตœ์ข…์ ์œผ๋กœ CompletableFuture ํด๋ž˜์Šค๊ฐ€ ํ•ด๋‹น task๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

 

AsyncTaskExecutor.submitCompletable() -> FutureUtils.callAsync() -> CompletableFuture.completableAsync()

 

์ด๋•Œ ์šฐ๋ฆฌ๋Š” ๋ณ„๋„์˜ executor๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ThreadPoolTaskExecutor์—์„œ ํ•ด๋‹น ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๋‹ค๋งŒ ์ด์ฒ˜๋Ÿผ ๋ณ„๋„์˜ executor๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ForkJoinPool์˜ CommonPool์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์šด์˜ ์‹œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์‚ฌ์šฉ์— ์ฃผ์˜ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค. (์ถœ์ฒ˜ : https://brunch.co.kr/@springboot/401 )

 

์Šคํ”„๋ง๋ถ€ํŠธ 3.0 ์ด์ „์—๋Š”..

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์Šคํ”„๋ง๋ถ€ํŠธ 2.7.8 ๋ฒ„์ „์˜ doSubmit() ๊ตฌํ˜„ ๋กœ์ง์œผ๋กœ returnType ์ด CompletableFuture ์ผ ๊ฒฝ์šฐ,

๋ช…์‹œ์ ์œผ๋กœ CompletableFuture.supplyAsync() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰ @Async ์–ด๋…ธํ…Œ์ด์…˜ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ๋Š” ๊ฒฐ๊ตญ CompletableFuture ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ ๋กœ์ง์ด ์ฒ˜๋ฆฌ๋˜๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 


์ •๋ฆฌ

์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋น„๋™๊ธฐ ํ˜ธ์ถœ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” @Async์˜ ๋™์ž‘์›๋ฆฌ๋ฅผ ์‚ดํŽด๋ดค๋‹ค.

์ •๋ฆฌํ•˜์ž๋ฉด

- @Async๋Š” @EnableAsync ๋ฅผ ํ•„์š”๋กœ ํ•˜๋ฉฐ ์ด๋Š” Proxy ๋ชจ๋“œ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค.

- ์ฆ‰ @Async ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Spring AOP ์ œ์•ฝ์‚ฌํ•ญ์„ ๋”ฐ๋ฅธ๋‹ค. ์ด๋•Œ pointcut ์€ @Async, advice๋Š” AnnotationAsyncExecutionInterceptor๊ฐ€ ๋œ๋‹ค.

- @Async ๋ฉ”์„œ๋“œ๋Š” AsyncExecutionInterceptor์—  ๋™์ž‘์ด ์ธํ„ฐ์…‰ํŠธ๋˜๊ณ  ๋ฆฌํ„ด ํƒ€์ž…์— ๋”ฐ๋ผ ๋น„๋™๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

- ๊ฒฐ๊ณผ์ ์œผ๋กœ @Async ์–ด๋…ธํ…Œ์ด์…˜์ด ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ๋Š” ๋‚ด๋ถ€์—์„œ CompletableFuture ํด๋ž˜์Šค์— ์˜ํ•ด ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•œ๋‹ค.

 

๊ฒฐ๊ตญ ์Šคํ”„๋ง์—์„œ @Async๋ฅผ ํ†ตํ•œ ๋น„๋™๊ธฐ๋กœ์ง์˜ ํ•ต์‹ฌ์€ CompletableFuture๋ผ๋Š” ๊ฒฐ๋ก ์„ ๋‚จ๊ธฐ๊ฒŒ ๋๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„  ์ด CompletableFuture์˜ ์ •์ฒด(?)์™€ ์›๋ฆฌ์— ๋Œ€ํ•ด ๋” ๊นŠ์ด ๋‹ค๋ค„ ๋ณผ ์˜ˆ์ •์ด๋‹ค.

 

 

 

 

ref

https://brunch.co.kr/@springboot/401

https://blog.naver.com/gngh0101/222073112894