👩🏻‍💻 Programming/SpringBoot

Spring과 비동기 처리

한국의 메타몽 2021. 8. 16. 06:01

 

동기와 비동기의 차이

 

먼저 동기와 비동기의 차이를 간단히 이해하자.

 

동기는 직렬형 작업 처리 모델이다. 작업은 순차적으로 시행되며 하나의 작업이 끝나기까지 다음 작업은 대기하게 된다.

예를 들어 DB에 SQL문을 날려서 데이터를 받아올 때, 데이터를 받아오기까지 다른 작업들은 수행되지 않는다.

 

비동기는 병렬형 작업 처리 모델입니다. 작업이 종료되지 않은 상태라도 대기하지 않고 다음 작업을 수행한다.

예를 들어 JavaScript의 DOM 이벤트와 타이머 함수, Ajax 요청은 비동기 처리 모델로 동작한다.

 

디테일한 설명은 이 글을 참고하면 확인할 수 있다.

 

동기(Syncronous)와 비동기(Asynchronous) 처리, 그리고 Callback

동기(Syncronous)와 비동기(Asynchronous) 링크 참고 : https://poiemaweb.com/js-async Asynchronous processing model | PoiemaWeb 동기식 처리 모델(Synchronous processing model)은 직렬적으로 작업을 수행한..

astrid-dm.tistory.com

 

 

Java에서 비동기란?

 

기본적으로 @EnableAsync와 @Async 어노테이션만으로 Java의 비동기를 구현할 수 있다.

Java에서 비동기에 대해 이야기하자면 정말 딥하게 다뤄질 수 있는데, 

여기서는 핵심적으로, 동시에 보편적으로 쓰일 수 있는 코드만 다루겠다.

 

[AsyncApplication]

@SpringBootApplication
@EnableAsync
public class AsyncApplication {

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

}

 

[AppConfig] - 스레드의 크기, 이름 등을 설정

@Configuration
public class AppConfig {
    @Bean("async-thread") // async-thread이름으로 bean등록
    public Executor asyncThead(){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 코어사이즈가 꽉차면 큐에 넣고, 큐마저도 꽉차면 코어 사이즈를 10개 더 늘리게 된다.
        threadPoolTaskExecutor.setMaxPoolSize(100); // 최대 갯수
        threadPoolTaskExecutor.setCorePoolSize(10); // 코어 사이즈
        threadPoolTaskExecutor.setQueueCapacity(10); // 큐 사이즈
        threadPoolTaskExecutor.setThreadNamePrefix("Async-"); // 접두사 설정
        return threadPoolTaskExecutor;
    }
}

 

[AsyncApplication]

@Slf4j
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class ApiController {
    private final AsyncService asyncService;
    @GetMapping("/hello")
    public CompletableFuture hello(){
        log.info("completable futre init");
        return asyncService.run();
    }
}

 

[AsyncService]

@Slf4j
@Service
public class AsyncService {

    @Async("async-thread")
    public CompletableFuture run(){ // 여러개의 API를 동시에 전송한 다음에 그 결과를 JOIN해서 받을때 사용하기 적절하다
        return new AsyncResult(hello()).completable();
    }

    public String hello() {
        for(int i=0; i<5; i++){
            try {
                Thread.sleep(2000);
                log.info("Thread sleep . . .");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "async hello";
    }
}

 

[결과]

지정된 이름의 스레드에서 프로세스가 돌고있음을 확인할 수 있다.

 

 

 

잘 안쓰인다

 

일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.

 

async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서

CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.

 

비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,

관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.

 

'👩🏻‍💻 Programming > SpringBoot' 카테고리의 다른 글

스프링과 JUnit  (0) 2021.08.19
RestTemplate  (0) 2021.08.16
Spring과 Filter - Interceptor (2)  (0) 2021.08.16
SpringBoo와 Filter, 그리고 Interceptor (1)  (0) 2021.08.16
Spring Boot와 Validation (1)  (0) 2021.08.11