본문 바로가기
개발/SpringBoot

SpringBoot @Async 사용

by 궁즉변 변즉통 통즉구 2021. 10. 27.
반응형

클라이언트 요청에 비동기 처리를 지원하는 SpringBoot @Async 사용법

 

기본적으로 Config에 @EnableAsync 어노테이션으로 Async사용을 활성화 시키고

비동기 처리가 필요한 서비스 메소드에 @Async 어노테이션을 작성하면 사용 가능하다.

 

@EnableAsync 애노테이션을 사용하면 기본 SimpleAsyncTaskExecutor를 사용하여 매번 쓰레드를 생성하는 방식이다.

쓰레드풀을 적용해서 처리를 할 경우 ThreadPoolTaskExecutor를 빈으로 등록해준다.

@Bean("myExecutor") 빈네임 설정을 통해 @Async("myExecutor") 어노테이션 사용시 풀을 지정할 수도 있다.

SpringBoot2.0 이상이면 application.yaml 파일로도 설정이 가능하다.

spring:
  task:
    execution:
      pool:
        core-size: 5
        max-size: 5
        queue-capacity: 10

- core-size: 동시에 실행 시킬 스레드 개수

- max-size: 스레드 풀의 최대 사이즈

- queue-capacity: core-size 개수를 넘어서는 task가 왔을 때 queue에 대기 시킬 task 개수

* 동작방식

요청이 들어오면 core-size 만큼의 스레드를 생성해서 동시 실행 시키고, 작업이 계속 실행중에 그 이상의 요청이 들어올 경우 먼저 queue에 queue-capacity 개수 만큼의 요청을 대기 시킨다. 그리고 queue-capacity 개수 이상이 넘어가는 요청이 계속 들어올 경우 그때 max-size 개수 만큼 스레드 생성해서 처리 진행

(core-size -> queue-capacity만큼 대기 -> max-size 스레드 생성)

 

 

이제 Service에서 아래와 같이 @Async 를 적용한 메소드를 선언하고 Controller에서 호출 테스트를 한다.

@Service
public class AsyncService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncService.class);

    @Async
    public void onAsync() throws Exception{
        Thread.sleep(5000);
        logger.info("Called Async");
    }
 }
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping({"/", "/async"})
    public String async(Model model) throws Exception{
        asyncService.onAsync();
        return "done";
    }
 }

 

리턴이 필요한 비동기 처리 샘플은 아래와 같다

    // Service
    @Async
    public CompletableFuture<String> returnOnAsync() throws Exception{
        Thread.sleep(5000);
        logger.info("Called Async");
        return new AsyncResult<>("Called Async").completable();
    }
    
    
    // Controller
    @GetMapping({"/", "/async_return"})
    public String async_return(Model model) throws Exception{
        CompletableFuture<String> future = asyncService.returnOnAsync();
        logger.info("pass 1!!");
        future.thenAccept(p -> logger.info("future : {}", p));
        logger.info("pass 2!!");
        return "done";
    }

테스트 결과 로그는 아래와 같이 찍힌다.

 

제약 사항

@Async의 제약사항은 @Transactional과 동일하게 기본적으로 Proxy방식으로 동작하여

- public method만 @Async가 가능하다. private는 불가능
- self-invocation(자가 호출)에서는 불가 => 같은 클래스 내부의 메서드를 호출하는 것은 안됨

반응형

댓글