TIL (20210823) - Spring @Async 비동기 서비스

Joshua_Kim·2021년 8월 24일
2

📖 TIL

목록 보기
8/8
post-thumbnail

🌱 서론

  • 오늘 출근하자마자 kakao template 프로젝트에서 delete 부분 수정 요구사항이 잔뜩 왔길래 오전에 하나둘 처리하고 나니 시간이 훅 가버렸다. (이 내용도 나중에 포스팅할 예정)

  • 거의다 처리했을 쯤 팀장님이 출근하셔서 보고드리고 나니 팀장님이 또 하나 공부할 keyword를 주셨다.

"민재씨 이제 Async가 뭔지 공부해봐요. 간단한거니까 금방 할거야"

  • 그렇게 해서 알아보게된 @Async 라는 녀석..🤔

  • 매우 간단한 Springboot 프로젝트를 만들어서 가지고 놀아봤다.

⭐️ @Async 비동기 서비스란?

  • 예를 들어보자.
    service에서 카톡메세지를 보내는 외부 api를 사용하고, 동시에 별개로 웹페이지에서 또 다른 동작을 해야하는 로직을 만들어야 한다고 가정해보자.
    Synchronized하게 로직이 진행이된다면 순차적으로 카톡메세지를 보내고나서, 그 다음 동작이 진행될 것이다. 그런데 이렇게 되면 별개의 동작인데 외부 api에서 혹여나 예외가 발생하거나 (외부 api장애로) 작업량이 많아서 트래픽이 걸리게 된다면 그 다음 동작은 괜시리 기다려야 한다.
    아니, 기다릴 뿐 아니라 그 다음 동작과 로직은 문제가 없는데, 별개로 진행되어야할 것이 막히거나 예외를 뱉어버리는 일이 생길 수 있다.

  • 이러한 문제를 해결하기 위해 Spring@Async를 제공하여 비동기 서비스를 구현할 수 있도록 해준다.

⚙️ 간단한 프로젝트를 만들어 봅시다.

🗝 전체 프로젝트 아웃라인

  • 프로젝트 구조는 이렇다. 매우 간단하다.
    config, controller, serviceApplication으로만 이루어진 매우 간단한 연습용 프로젝트다.

🗝 config

AsyncConfig.java

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig extends AsyncConfigurerSupport {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("joshua-async-");
        executor.initialize();
        return executor;
    }
}
  • AsynConfigurerSupport를 상속받아서 getAsyncExecutor()Override해준다.

  • ThreadPoolTaskExecutor의 인스턴스를 통해 세팅을 해준다.

  • setting
    corePoolSize : 실행을 대기하고 있는 thread의 개수 설정
    maxPoolSize : 동시에 동작하는 최대 thread의 개수 설정
    queueCapacity : maxPoolSize를 초과하는 요청이 들어오게 되면 해당 내용을 Queue에 저장하고, 사용할 수 있는 thread 여유가 발생하면 하나씩 꺼냄
    threadNamePrefix : Spring이 생성해주는 thread의 접두사를 지정

🗝 service

AsyncUtilService.java

@Service
public class AsyncUtilService {
    @Async
    public void run (Runnable runnable) {
        runnable.run();
    }
}
  • 내무 메소드 사용시 비동기 로직으로 만들고 싶을 때 사용할 utilservice를 하나 만들어 줬다.

AsyncService.java

@Service
@Slf4j
@RequiredArgsConstructor
public class AsyncService {
    private final AsyncUtilService asyncUtilService;
    @Async
    public void onAsync () {
        try {
            Thread.sleep(5000);
            log.info("on Async");
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void onSync() {
        try {
            Thread.sleep(5000);
            log.info("on Sync");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void onAsyncWrapping () {
        asyncUtilService.run(() -> onAsync());
         asyncUtilService.run(this::onAsync);
    }
}
  • 정말 사용하기 간편하다.

  • @Async 어노테이션을 붙여주면 해당 메소드는 비동기로 동작한다.

  • onAsyncWrapping() 메소드는 내부 메소드에서 재사용시 비동기로 사용하고 싶을 때 쓰기위해서 만들어 놓은 것을 테스트하기 위함이다.
    이 메소드는 @Async 어노테이션이 없지만, AsyncUtilService 클래스의 run 메소드를 통하여 파라미터로 실행시키고 싶은 비동기 메소드를 받아 처리할 수 있다.

🗝 controller

AsyncController.java

@RestController
@RequiredArgsConstructor
@Slf4j
public class AsyncController {
    private final AsyncService service;
    @GetMapping ("/async")
    public String async () {
        service.onAsyncWrapping();
        String result = "Async spring boot";
        log.info(result);
        log.info("-----------------------------");
        return result;
    }
    @GetMapping ("/sync")
    public String sync () {
        service.onSync();
        String result = "Sync spring boot";
        log.info(result);
        log.info("-------------------------------");
        return result;
    }
  • GetMapping으로 간단하게 실험하였더니 매우 잘 동작한다.

🙏 오늘의 TIL을 마치며

  • 이제 Async도 공부했겠다, 카카오톡 메세지를 비동기로 보내는 트리거 서비스를 만들어 보려고한다. 팀장님의 하드트레이닝

  • 그럼 오늘은 이만 ~~ ✋

profile
인문학 하는 개발자 💻

0개의 댓글