Today
-
Yesterday
-
Total
-
  • spring boot: 비동기 처리하기
    Spring Boot 🍃 2023. 12. 5. 00:01

    보고 배운 인강 링크

    웹 MVC에서는 많이 사용할 일이 없다고 하지만,

    비동기에 대한 호기심을 풀어내기 위해 간단히 학습!


    만약 DB를 사용할 때 비동기를 사용해야 한다면

    NoSQL을 사용할 때는 Spring WebFlux 를 사용하는 것을 추천

    RDB를 사용하게 된다면 여기서 사용하는 @Async 가 소용이 없다. 왜냐하면 트랜잭션 때문에 동기 방식으로 통신을 해야하기 때문에 전체적인 flow 자체가 비동기가 될 수 없다고 한다.

    이러한 부분들은 spring 보다는 웹의 난이도, 아키텍처의 난이도가 올라갔을 때 경험해보게 될 수도 있는 부분이라고 강사님이 설명해주셨다.

    이번 강의는 Spring WebFlux가 아닌, WebMvc에서도 이런식으로 코딩이 가능하다는 것을 체험해보기 위한 강의!


    간단 실습

    1. Application 파일에 어노테이션 추가

    @EnableAsync 어노테이션을 추가해주어야 한다.

    @SpringBootApplication
    @EnableAsync
    public class AsyncApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(AsyncApplication.class, args);
        }
    
    }

    2. Service 파일 작성

    간단한 테스트를 위해 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";
        }
    }

    3. Controller 파일 작성

    @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();
        }
    
    }

    (안해도 됨) 4. Spring Thread Pool 설정

    이 부분은 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;
        }
    }

    5. 실행 !

    Request API를 이용해 controller의 주소로 요청을 보내면 응답을 기다리는 (코딩을 그렇게 함) 상태가 된다.

    스레드 동작 확인 메인 스레드에서 동작하지 않고, 새로운 스레드에서 동작하는 것을 확인할 수 있다. 그리고 AppConfig 파일에서 설정해준 name prefix 가 붙은 스레드가 실행되어 동작하는 것까지 확인!

    스레드 실행이 끝나면 반환 값이 request api에 출력되는것을 확인할 수 있다.

    'Spring Boot 🍃' 카테고리의 다른 글

    Spring boot: RestTemplate - Naver 지역 검색 api 연동해보기  (0) 2023.12.05
    spring boot : RestTemplate  (0) 2023.12.05
    Spring Boot: Interceptor 인터셉터  (0) 2023.12.05
    spring boot: Slf4j  (0) 2023.12.05
    Spring Boot: Filter  (0) 2023.12.04

Designed by Tistory / Custom by 얼거스