오답노트

[Spring] 비동기 처리 (Async) 본문

Java/Spring

[Spring] 비동기 처리 (Async)

권멋져 2023. 7. 14. 14:41

Async

비동기 처리는 thread를 통해 다른 작업을 처리하게 만들고 기존 로직은 그대로 실행시키게 하는 것이다.

 

@SpringBootApplication
@EnableAsync
public class AsyncApplication {

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

}

메인 클래스에서 EnableAsync Annotation을 사용하요 비동기를 사용하는 것을 명시한다.

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class AsycService {

    @Async
    public String hello(){
        for(int i = 0 ; i < 10 ; i++){
            try{
                Thread.sleep(2000);
                log.info("thead sleep");
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        return "hello";
    }
}

위 처럼 클래스를 정의하고 Service Annotation으로 Spring에 등록한다. 그리고 메소드 중 비동기로 작업할 메소드에 Async Annotation을 사용한다.

 

CompletableFuture

비동기의 특성 상 비동기에서 반환되는 값을 받는 타이밍과 로직의 진행 타이밍은 다를 수 밖에 없다.

하지만 비동기에서 반환되는 값을 로직에서 활용해야하는 경우가 있는데 이때 사용하는 것이 CompletableFuture다.

@Service
public class AsycService {

    @Async()
    public CompletableFuture run(){
        return new AsyncResult(hello()).completable();
    }
    @Async
    public String hello(){
        for(int i = 0 ; i < 10 ; i++){
            try{
                Thread.sleep(2000);
                log.info("thead sleep");
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        return "hello";
    }
}

hello 를 반환하는 비동기 함수 run을 만들었다.

 

import com.example.async.service.AsycService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
public class ApiController {

    private final AsycService asycService;

    @GetMapping("/hello")
    public CompletableFuture hello(){
        log.info("CompletableFuture init");
        return asycService.run();
    }
}

API 에서 마찬가지로 CompletableFuture를 반환하는 함수로 되어있다. 

 

Thread 할당

thread 할당을 정의하지 않으면 Spring이 알아서 해주긴 하지만 사용자가 설정하여 얼마나 어떻게 thread를 할당할지 정의할 수 있다.

 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class AppConfig {
    @Bean("async-thread")
    public Executor asyncThread(){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setMaxPoolSize(100);
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setQueueCapacity(10);
        threadPoolTaskExecutor.setThreadNamePrefix("Async-");

        return threadPoolTaskExecutor;
    }
}

CorePoolSize는 현재 운용중인 Thread가 부족할 때 추가될 Thread의 개수이다. 하지만 바로 추가되는 것이 아니라 작업을 Queue에 먼저 넣어 놓는다. 만약 작업이 밀려 할당한 Queue에 작업이 꽉 차 있다면, 그때 Thread의 개수를 추가한다. Thread의 최대 개수는 100개 이다.

 

예를 들면 Thread 10개가 작업을 하고 있는데 추가로 5개가 들어왔으면 Thread는 추가되지 않고 5개는 대기 상태이다.

이때, 추가로 10개가 들어와서 총 15개의 작업이 대기해야하는 상황이면 Thread를 20개로 늘리게된다.

'Java > Spring' 카테고리의 다른 글

[Spring] Server to Server 예제 (네이버 API)  (0) 2023.07.14
[Spring] Server to Server  (0) 2023.07.14
[Spring] Interceptor  (0) 2023.07.14
[Spring] Filter  (0) 2023.07.14
[Spring] Exception과 Validation  (0) 2023.07.14