오늘 출근하자마자 kakao template
프로젝트에서 delete
부분 수정 요구사항이 잔뜩 왔길래 오전에 하나둘 처리하고 나니 시간이 훅 가버렸다. (이 내용도 나중에 포스팅할 예정)
거의다 처리했을 쯤 팀장님이 출근하셔서 보고드리고 나니 팀장님이 또 하나 공부할 keyword
를 주셨다.
"민재씨 이제
Async
가 뭔지 공부해봐요. 간단한거니까 금방 할거야"
그렇게 해서 알아보게된 @Async
라는 녀석..🤔
매우 간단한 Springboot
프로젝트를 만들어서 가지고 놀아봤다.
예를 들어보자.
service
에서 카톡메세지를 보내는 외부 api
를 사용하고, 동시에 별개로 웹페이지에서 또 다른 동작을 해야하는 로직을 만들어야 한다고 가정해보자.
Synchronized
하게 로직이 진행이된다면 순차적으로 카톡메세지를 보내고나서, 그 다음 동작이 진행될 것이다. 그런데 이렇게 되면 별개의 동작인데 외부 api
에서 혹여나 예외가 발생하거나 (외부 api
장애로) 작업량이 많아서 트래픽이 걸리게 된다면 그 다음 동작은 괜시리 기다려야 한다.
아니, 기다릴 뿐 아니라 그 다음 동작과 로직은 문제가 없는데, 별개로 진행되어야할 것이 막히거나 예외를 뱉어버리는 일이 생길 수 있다.
이러한 문제를 해결하기 위해 Spring
은 @Async
를 제공하여 비동기 서비스를 구현할 수 있도록 해준다.
config, controller, service
와 Application
으로만 이루어진 매우 간단한 연습용 프로젝트다.
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
의 접두사를 지정
AsyncUtilService.java
@Service public class AsyncUtilService { @Async public void run (Runnable runnable) { runnable.run(); } }
util
성 service
를 하나 만들어 줬다.
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
메소드를 통하여 파라미터로 실행시키고 싶은 비동기 메소드를 받아 처리할 수 있다.
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
으로 간단하게 실험하였더니 매우 잘 동작한다.
이제 Async
도 공부했겠다, 카카오톡 메세지를 비동기로 보내는 트리거 서비스를 만들어 보려고한다. 팀장님의 하드트레이닝
그럼 오늘은 이만 ~~ ✋