웹 MVC에서는 많이 사용할 일이 없다고 하지만,
비동기에 대한 호기심을 풀어내기 위해 간단히 학습!
만약 DB를 사용할 때 비동기를 사용해야 한다면
NoSQL
을 사용할 때는 Spring WebFlux
를 사용하는 것을 추천
RDB를 사용하게 된다면 여기서 사용하는 @Async 가 소용이 없다.
왜냐하면 트랜잭션 때문에 동기 방식으로 통신을 해야하기 때문에
전체적인 flow 자체가 비동기가 될 수 없다고 한다.
이러한 부분들은 spring 보다는
웹의 난이도, 아키텍처의 난이도가 올라갔을 때 경험해보게 될 수도 있는 부분이라고
강사님이 설명해주셨다.
이번 강의는 Spring WebFlux가 아닌,
WebMvc에서도 이런식으로 코딩이 가능하다는 것을 체험해보기 위한 강의!
@EnableAsync
어노테이션을 추가해주어야 한다.
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
간단한 테스트를 위해 service 파일에서
비동기 동작을 하는 api 격으로 hello() 메서드를 작성해주고,
hello() 메서드를 실행해서 controller로 결과값을 보낼 run() 메서드를 작성해준다.
run() 메서드에는 @Async
어노테이션을 붙여준다.
이 때, @Async 어노테이션은 Proxy 패턴을 타기 때문에 public 메서드에만 사용할 수 있다.
@Async 어노테이션의 옵션으로, 등록된 bean의 이름을 입력해 줄 수 있음.
이 때 bean은 thread pool 설정 bean임!
그리고 지금은 간단한 사용 경험을 위해서 간단하게 작성했지만,
CompletableFuture를 사용할 경우
여러개 . 3~4개의 api를 CompletableFuture로 받은 다음 ,
결과 값을 합쳐서 사용하는 형태를 권장한다.
@Slf4j
@Service
public class AsyncService {
@Async("async-thread")
public CompletableFuture<String> run() {
return CompletableFuture.completedFuture(hello());
}
public String hello() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(2000);
log.info("thread sleep ... {}", i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "async hello";
}
}
@Slf4j
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class ApiController {
private final AsyncService asyncService;
@GetMapping("/hello")
public CompletableFuture<String> hello() {
log.info("completable future init");
return asyncService.run();
}
}
이 부분은 Spring의 Thread Pool 관리
에 대해 잘 알고 나서 건드려야 좋다.
(일단 궁금해서 한 번만 찾아본 정리 잘 된 것 같은 링크 : jaehoney.tistory)
Thread Pool 관련 내용이 무척 방대하고,
거대한 프로젝트가 아니라면 사용할 일이 거의 없기 때문에
지금은 이렇게 사용하는구나~ 정도로만 알아두고
추후에 공부할 게 없을 때나, 실무에서 스레드를 구현하게 되었을 때 공부해도 늦지 않다고 한다!
Spring의 Thread Pool에 대해 알고난 후 ThreadPoolTaskExecutor 학습 추천
@Configuration
public class AppConfig {
@Bean("async-thread")
public Executor asyncThread() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setMaxPoolSize(100);
threadPoolTaskExecutor.setCorePoolSize(10); // core 10개를 다 사용하면 queue에 내용이 들어감
threadPoolTaskExecutor.setQueueCapacity(10); // queue까지 10개가 다 차면 core 크기만큼 한번 더 늘어남
threadPoolTaskExecutor.setThreadNamePrefix("Async-"); // 스레드에 시작 이름 붙임
return threadPoolTaskExecutor;
}
}
Request API를 이용해 controller의 주소로 요청을 보내면 응답을 기다리는 (코딩을 그렇게 함) 상태가 된다.
스레드 동작 확인
메인 스레드에서 동작하지 않고, 새로운 스레드에서 동작하는 것을 확인할 수 있다.
그리고 AppConfig 파일에서 설정해준
name prefix 가 붙은 스레드가 실행되어 동작하는 것까지 확인!
스레드 실행이 끝나면
반환 값이 request api에 출력되는것을 확인할 수 있다.