๋ฌธ์ ์ํฉ
ํ๋ก์ ํธ QA์, ์ฃผ๊ฐ ๋ฆฌํฌํธ ๊ธฐ๋ฅ์ ์๋ต ์๊ฐ์ด ์ฌ์ฉ์ 1๋ช ๋น ์ต์ ์ ๊ฒฝ์ฐ 2๋ถ ์ด์, ๋์ฒด๋ก ํ๊ท 1๋ถ ์ด์ ์์๋๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ๋ถ์ํ ๊ฒฐ๊ณผ, DB ์ฟผ๋ฆฌ๋ ์คํ ๊ณํ์ eq_ref ์ ref๋ก ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌ๋๊ณ ์์์ต๋๋ค.
์๋ต ์๊ฐ์ด ๊ธธ์ด์ง ์ฃผ์ ์์ธ์ ๋น์ฆ๋์ค ๋ก์ง์์ ์ต๋ 7๋ฒ์ ์ธ๋ถ API ํธ์ถ์ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์๊ธฐ ๋๋ฌธ์ด์์ต๋๋ค.
์๋ ์ฝ๋์ฒ๋ผ, stream() ๋ด๋ถ์์ ์ฐ์์ ์ธ ์ธ๋ถ API ํธ์ถ์ ์ฒ๋ฆฌํ๋ฉด์ ์ผ์ผ ์๋น์ค์ Clova AI๊ฐ ๋๊ธฐ์ ์ผ๋ก ์์ฒญ๊ณผ ์๋ต์ ์ฃผ๊ณ ๋ฐ๋ ๊ตฌ์กฐ๋ก ๋์ํ๊ณ ์์๊ณ , ์ด๋ฅผ ๊ฐ์ ํ๊ณ ์ ํ์ต๋๋ค.
Map<์ธ๋ถ API ๊ฒฐ๊ณผ, List<Value>> valuesByKey = map.values().stream() <<-- ์ธ๋ถ API ์๋ต collect
.collect(Collectors.toMap(
values -> ์ธ๋ถ API ํธ์ถ, <<-- ์ธ๋ถ API ํธ์ถ
values -> values));
3 ๊ฐ์ง ๋์ ๋ฐฉ์
๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด 3 ๊ฐ์ง ๋์์ ๊ณ ๋ คํ์ต๋๋ค.
- parallel()์ ์ฌ์ฉํ์ฌ ์คํธ๋ฆผ์ ๋ด๋ถ์ ์ผ๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌํ๋ค.
- ์ธ๋ถ API ํธ์ถ์ @Async / CompletableFuture ๋ฅผ ํ์ฉํด ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
- ์ธ๋ถ API ํธ์ถ ๋ฐฉ์์ Non-Blocking I/O๋ก ๋ณ๊ฒฝํ๋ค.
- ํ์ฌ ์ฌ์ฉ ์ค์ธ OpenFeign ๋์ Spring WebFlux์ WebClient๋ก ๊ต์ฒดํ๋ ๋ฑ ์๋ก์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์ ํ๋ค.
์ด ์ค 2๋ฒ, @Async / CompletableFuture ๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ํธ์ถ ๋ฐฉ์์ ์ ํํ ์ด์ ์ ๋ค๋ฅธ ๋์์ ๋ฐฐ์ ํ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1๋ฒ์ธ parallel() ์ ์ฌ์ฉํ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ๋ฐฐ์ ํ ์ด์
parallel() ์ ์ฌ์ฉํ์ฌ ์คํธ๋ฆผ์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ์ง ์์ ์ด์ ๋ paralle(๋ณ๋ ฌ ์คํธ๋ฆผ)์ ๋์ ๋ฐฉ์์ CPU ์์ ์ ํจ์จ์ ์ด๋ฉฐ, I/O ์์ (I/O-bound)์ ์ ํฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
- parallel() ์ ์ฌ์ฉํ๋ฉด ์๋ฐ๋ ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ๋ฅผ ํ์ฉํด ์คํธ๋ฆผ์ ์ฌ๊ท์ ์ผ๋ก ๋ถํ ํ๊ณ (fork), ๊ฐ๊ฐ์ ์ฐ๋ ๋์์ ๋ถํ ํ ์คํธ๋ฆผ์ ์์ ํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด ๋ ๋๋ค. ์ด ํ, ์ด ๊ฒฐ๊ณผ๋ค์ ํฉ์น๊ฒ ๋ฉ๋๋ค.(join)
- ์ด ๋, ์คํธ๋ฆผ์ ์์ ์ ์ํํ ์ฐ๋ ๋๋ค์ ForkJoinPool ํตํด ์์ฑํ๋ฉฐ ForkJoinPool ์ JVM์ด ์ฌ์ฉํ ์ ์๋ CPU ์ฝ์ด ์์ ๋ง์ถฐ ์ฐ๋ ๋๋ฅผ ์์ฑํฉ๋๋ค.
int threads = Runtime.getRuntime().availableProcessors();
- ๊ทธ๋ฆฌ๊ณ , ๊ฐ๊ฐ์ ์ฐ๋ ๋๋ค์ ๋ถํ ๋ stream() ์์ ์์ ์ ์ฐ์ฐ์ ๋ง์ณ์ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด ๋๋ค๋ฉด ์์ ํ์น๊ธฐ(Work Stealing) ๋ฅผ ํตํด ๋ค๋ฅธ ์ค๋ ๋์ ์์ ์ "ํ์ณ์" ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ฅผ ํตํด CPU ์ฌ์ฉ๋ฅ ์ ๊ทน๋ํํ๋ ค๊ณ ํฉ๋๋ค.
๋ฐ๋ฉด I/O ์์ ์ ์ฐ๋ ๋๊ฐ Blocking ์ํ๋ก ์ธํด ์ค๋ ๋๊ฐ ๋๊ธฐํ๋ ์๊ฐ์ด ๋ง์, ์์ ์ ๋๋ ์ฌ์ง๊ฐ ์ ์ต๋๋ค. ์ฆ, ์์ ์ "ํ์ณ์ค๋" ๊ฒ์ด ํฐ ์๋ฏธ๋ฅผ ๊ฐ์ง ๋ชปํ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋์, "๋ชจ๋ ์๋ฐ ์ธ ์ก์ "์์๋ ์ ๋ฌธ์ ์ํฉ์ฒ๋ผ ๋ชจ๋ ์์ ์ด I/O ์์ ์ผ ๊ฒฝ์ฐ, ๋น๋๊ธฐ API๋ฅผ ํ์ฉํ๋ ๋ชจ์ต์ ๋ณผ ์ ์์์ต๋๋ค.
(์ด๋ฌํ ์ด์ ๋ก ForkJoinPool์ Work Stealing ์ ๋ํ ์๊ฐ๋ฅผ ๋ณด์๋ง์ ํด๋น ๋ฐฉ์์ ๋ง์ง ์๊ตฌ๋๋ฅผ ์ ์ ์๊ฒ ๋์์ต๋๋ค. Work Stealing ๊ณผ ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ๋ ๋ค์๋ฒ์ ๊ณต๋ถํ๋ ๊ฑธ๋ก..)
3๋ฒ์ธ Non-Blocking I/O(WebClient)๋ก์ ๊ต์ฒด๋ฅผ ๋ฐฐ์ ํ ์ด์
์ฆ๊ฐ์ ์ธ ์ ์ฉ ์ด๋ ค์
- WebClient์ ๋์ ๋ฐฉ์์ ์์ ํ ํ์ตํด์ผ ํ๋ฉฐ, ์ด๋ ํ์ฌ ์ฑ๋ฅ ๊ฐ์ ์ด ๊ธด๊ธํ ์๊ตฌ๋๋ ์ํฉ์์ ํ๋ก์ ํธ๋ฅผ ์ง์ฐ์ํฌ ์ ์์ต๋๋ค
ํ์์ ํ์ต ๋น์ฉ ์ฆ๊ฐ
- ํ์ ํ๋ก์ ํธ์์ OpenFeign์ ์ฃผ๋ก ์ฌ์ฉ ์ค์ด์๊ธฐ ๋๋ฌธ์, WebClient๋ก ๊ต์ฒดํ ๊ฒฝ์ฐ ํ์๋ค์ด WebClient์ ๋์ ๋ฐฉ์์ ์ตํ๋ ๋ฐ ์ถ๊ฐ์ ์ธ ํ์ต ์๊ฐ์ด ํ์ํฉ๋๋ค.
2๋ฒ์ธ '@Async / CompletableFuture ๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ' ์ ํํ ์ด์
๋น ๋ฅธ ์ ์ฉ ๊ฐ๋ฅ
- ํ์ ๋ชจ๋๊ฐ ๊ฐ์ ์ฝ๋์ค์ฟผ๋ ์๋ฃ์์ด์์ผ๋ฉฐ, ํด๋น ๊ต์ก ๊ธฐ๊ด์์ CompletableFuture ๋ฅผ ํ์ฉํ ๊ฒฝํ์ด ์์ต๋๋ค.
- ๋น๋๊ธฐ์ ๋ํด ๊ธฐ๋ณธ์ ์ธ ์ง์์ ๋ชจ๋ ๊ฐ์ง๊ณ ์์์ต๋๋ค.
๋ฌผ๋ก , 2๋ฒ๋ ๊ณ ๋ คํ ์ ์ด ์์ต๋๋ค. ๋น๋๊ธฐ๋ฅผ ์ํด ์ฐ๋ ๋ ํ์ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ, ๋์ ์์ฒญ์ด ๋ง์ ๊ฒฝ์ฐ ์ค๋ ๋ ํ์ ํ ์ฉ๋์ด ๊ฐ๋ ์ฐจ๋ฉด์ ํด๋ผ์ด์ธํธ๊ฐ ๋๊ธฐํด์ผ ํ๋ ์ํฉ์ด ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ์ํฉ์์๋ ๋ณ๋ชฉ ํ์์ด @Async์ ์ค๋ ๋ ํ ๋๋ฌธ์ธ์ง, ๋ค๋ฅธ ์ง์ ์์ ๋ฐ์ํ๋์ง๋ฅผ ํ์ ํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด ๊ธฐ์กด์ ๊ตฌ์ถํ Prometheus์ Grafana๋ฅผ ํ์ฉํด ๋ฌธ์ ๋ฅผ ๋ชจ๋ํฐ๋งํ๊ณ ๋ถ์ํ ๊ณํ์ ์์ต๋๋ค.
2๋ฒ ๋ฐฉ๋ฒ ์ฐ๋ ๋ ํ ์ค์ ๊ณผ์
๋จผ์ ์ฝ๋๋ฅผ ๋ณด์ฌ๋๋ฆฌ๊ณ , ๊ทธ์ ๋ํ ์ค๋ช ์ ์ด์ด๋๊ฐ๊ฒ ์ต๋๋ค.
๋จผ์ , ์ฐ๋ ๋ ํ ์ค์ ์ฝ๋์ ๋๋ค.
@EnableAsync
@Configuration
public class AsyncConfig {
@Bean
public Executor clovaExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(16);
executor.setMaxPoolSize(32);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(60); // ๊ธฐ๋ณธ ๊ฐ 60 ์ด์ง๋ง, ๋ช
์์ ์ผ๋ก ์๊ธฐ ์ํจ
executor.setThreadNamePrefix("clova-async-");
executor.setAwaitTerminationSeconds(60);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setRejectedExecutionHandler(new CallerRunsPolicy());
executor.initialize();
}
}
@EnableAsync ์ค๋ช
@EnableAsync๋ Spring์์ ๋น๋๊ธฐ ์์ ์ ํ์ฑํํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ์ด๋ ธํ ์ด์ ์ ๋๋ค. Spring์ ๋น๋๊ธฐ ๋ฉ์๋ ์คํ ์ ์ด๋ฅผ ์ฒ๋ฆฌํ ์ค๋ ๋ ํ์ ๋ค์๊ณผ ๊ฐ์ ์์๋ก ๊ฒ์ํฉ๋๋ค.
- ์คํ๋ง ์ปจํ ์คํธ์ ์ ์ผํ org.springframework.core.task.TaskExecutor ๋น์ด ์๊ฑฐ๋, java.util.concurrent.Executor ๋น์ ์ด๋ฆ์ด "taskExecutor"๋ผ๋ฉด ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ๋ง์ฝ ์์ ๋ ๊ฐ์ง ์กฐ๊ฑด์ ํด๋นํ๋ ๋น์ด ์์ผ๋ฉด, Spring์ org.springframework.core.task.SimpleAsyncTaskExecutor๋ฅผ ์ฌ์ฉํด ๋น๋๊ธฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
- SimpleAsyncTaskExecutor ๋ ๋น๋๊ธฐ ์์ ์์ฒญ์ ์ฒ๋ฆฌํ ๋๋ง๋ค ์ฐ๋ ๋๋ฅผ ์์ฑํ๋ฏ๋ก ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
-- @EnableAsync ์ Javadoc ์ผ๋ถ
By default, Spring will be searching for an associated thread pool definition: either a unique org.springframework.core.task.TaskExecutor bean in the context, or an java.util.concurrent.Executor bean named "taskExecutor" otherwise. If neither of the two is resolvable, a org.springframework.core.task.SimpleAsyncTaskExecutor will be used to process async method invocations. Besides, annotated methods having a void return type cannot transmit any exception back to the caller. By default, such uncaught exceptions are only logged.
Spring Boot ์ @EnableAsync
ํ์ง๋ง Spring Boot ์ ํจ๊ป @EnableAsync ์ฌ์ฉ ์, ์ฐ๋ ๋ ํ ์ค์ ์ ๋ฐ๋กํ์ง ์์๋ SimpleAsyncTaskExecutor ๋์ Auto configuration ์ ์ํด ๋น์ผ๋ก ๋ฑ๋ก๋ ์ฐ๋ ๋ ํ์ด ์ฐ์ ๋๋ค.
Spring Boot 2.1 ๋ถํฐ ์๋ ๊ตฌ์ฑ์ ์ํด org.springframework.boot.autoconfigure.task์ TaskExecutionProperties ๊ฐ ์ ์ฉ๋ฉ๋๋ค. ์ด ์ค์ ์ ๊ธฐ๋ณธ๊ฐ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ๊ธฐ๋ณธ ์ฐ๋ ๋ ์: 8
- ์ต๋ ์ฐ๋ ๋: INTEGER.MAX_VALUE
- ํ ์ฉ๋: INTEGER.MAX_VALUE
- ์ต๋ ์ฐ๋ ๋์ ์ด์์๋ ์๊ฐ: 60์ด
๊ทธ๋ฌ๋, ์ด ์ค์ ์ ์๋ฒ ์คํ๊ณผ ๋ง์ง ์์ ์ ์์ผ๋ฏ๋ก, ์๋ฒ ์คํ์ ๋ง๊ฒ ์ปค์คํฐ๋ง์ด์งํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๋ํ, ์คํ๋ง ๋ถํธ๋ ์๋ ์ฝ๋์ฒ๋ผ ์ฐ๋ ๋ ํ์ 2๊ฐ ์ด์ ์ค์ ์, @Async ์ด๋ ธํ ์ด์ ์ ์ด๋ค ์ฐ๋ ๋ ํ์ ์ฌ์ฉํ ๊ฑด์ง ์ ์ํ์ง ์์ผ๋ฉด @Async ์ฒ๋ฆฌํ๋๋ฐ SimpleAsyncTaskExecutor ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ ์ฃผ์ํด์ผ ํ ํ์๊ฐ ์์ต๋๋ค.
@Bean
public Executor asyncExecutor1() {
ThreadPoolTaskExecutor executor1 = new ThreadPoolTaskExecutor();
return executor1;
}
@Bean
public Executor asyncExecutor2() {
ThreadPoolTaskExecutor executor2 = new ThreadPoolTaskExecutor();
return executor2;
}
...
@Async // asyncExecutor1 ๊ณผ aysncExecutor2 ์ค ์ด๋ค ์ฐ๋ ๋ ํ์ ์ฌ์ฉํ ๊ฒ์ธ์ง ๋ช
์ํด์ผ ํ๋ค.
public void doAsync()...
์ฐ์ํ ์ข ๋ฃ๋ฅผ ์ํ ThreadPoolTaskExecutor ์ฌ์ฉ
๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด Spring์ ThreadPoolTaskExecutor๋ฅผ ์ฌ์ฉํ์ต๋๋ค. ์ด๋ Java์ ThreadPoolExecutor๋ฅผ Spring ์๋ช ์ฃผ๊ธฐ์ ๋ง๊ฒ ํ์ฅํ ๊ตฌํ์ฒด๋ก, ์ฐ์ํ ์ข ๋ฃ(graceful shutdown)๋ฅผ ์ง์ํฉ๋๋ค.
ThreadPoolTaskExecutor๋ ์ฐ๋ ๋ ํ ์ข ๋ฃ ๋ฉ์๋์ธ shutdown()์ Spring์ ๋ผ์ดํ์ฌ์ดํด๊ณผ ๊ด๋ จ์ง์ด ์ถ์ํํ์ฌ ์ ๊ณตํจ์ผ๋ก์จ, ์ฐ๋ ๋ ํ์ ์ฐ์ํ ์ข ๋ฃ๋ฅผ ์คํ๋ง ๋ผ์ดํ์ฌ์ดํด์ ๋ง์ถฐ ์ฝ๊ฒ ์ค์ ํ ์ ์์ต๋๋ค.
์ฐ์ํ ์ข ๋ฃ๋?
- ์๋ฒ ์ข ๋ฃ ์, ์ค๋ ๋ ํ์ ์์ ์ค์ธ ์ค๋ ๋์ ๋๊ธฐ ์ค์ธ ํ์ ์์ ์ด ๋ชจ๋ ์๋ฃ๋ ๋๊น์ง ์ค๋ ๋ ํ์ ์ข ๋ฃ๋ฅผ ์ง์ฐ์ํค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
- ๋ง์ฝ ์๋ฒ๊ฐ ์ข ๋ฃ ์ค์ ์ค๋ ๋ ํ์ด ๊ฐ์ ๋ก ์ข ๋ฃ๋๋ค๋ฉด I/O ์์ ์ค์ด๋ ์ค๋ ๋๊ฐ ์์ ์ ์๋ฃํ์ง ๋ชปํ๊ฑฐ๋, ํ์ ๋๊ธฐ ์ค์ด๋ ์์ ์ด ์ฒ๋ฆฌ๋์ง ๋ชปํ ์ ์์ต๋๋ค.
- ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์ค๋ ๋์ ํ์ ๋ชจ๋ ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ ํ ์ข ๋ฃํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๋ค๋ง, ์ฐ์ํ ์ข ๋ฃ ๊ณผ์ ์์ ์์ ์ ์ข ๋ฃ ์๊ฐ์ด ์ง๋์น๊ฒ ๊ธธ์ด์ง ๊ฒฝ์ฐ ์๋น์ค๊ฐ ์ข ๋ฃ๋์ง ๋ชปํ๋ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ต๋๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด, ์์ ์๋ฃ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ต๋ ์๊ฐ์ ์ค์ ํด์ ๋๊ธฐ ์๊ฐ์ด ์ด๊ณผ๋๋ฉด ์ค๋ ๋ ํ์ด ๊ฐ์ ๋ก ์ข ๋ฃ๋๋๋ก ํ์ต๋๋ค.
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskExecutor.setAwaitTerminationSeconds(60);
(++์ฒ์์๋ ๋ถ๋ช ํ ์ฐ์ํ ์ข ๋ฃ๋ฅผ ์ง์ํด์ฃผ๋ ๋ฉ์๋๊ฐ ์์ํ ๋ฐ ํ๋ฉฐ ThreadPoolTaskExecutor์ Javadoc ์ ํด์ํ๋ฉฐ ์๊ฐ์ ๋ง์ด ์๋นํ๊ณ , ์ ๋ฆฌํ ์๋๊ฐ ์๋ฌ์๋๋ฐ ์ฐ์ํ ์ข ๋ฃ๋ฅผ ์ด๋ป๊ฒ ์ง์ํด์ฃผ๋์ง, ์ ๋ฆฌ๊ฐ ์ ๋ ๊ธ์ ์ฐพ์ ๊ณต์ ํฉ๋๋ค. ThreadPoolTaskExecutor์ waitForTasksToCompleteOnShutdown ์์ฑ ์์๋ณด๊ธฐ)
I/O bound ์ ๋ง๊ฒ ์ฐ๋ ๋ ํ์ ์ฐ๋ ๋ ์ค์
์ผ๋ฐ์ ์ผ๋ก I/O ์์ ์ด ๋ง์ ๊ฒฝ์ฐ, ์ฐ๋ ๋ ํ์ coreSize๋ ์ฝ์ด์ 2~3๋ฐฐ๋ก ์ค์ ํ ์ ์์ต๋๋ค.([Youtube] ์ฌ์ด ์ฝ๋ - ์ค๋ ๋ ํ ์ค์ )
ํ์ง๋ง, ์ ํํ ๊ฐ์ ์๋ ๊ณต์์ ๊ณ์ฐํด์ผ ํฉ๋๋ค. ๋ค๋ง, ์ ๋ ์๋ ๊ณต์์ ์ด๋ค ์ํฉ์์์ CPUํ์ฉ ๋น์จ์ ์ธก์ ํด์ผ ํ๋์ง, ๋๊ธฐ์๊ฐ๊ณผ ๊ณ์ฐ์๊ฐ์ ์ธ์ ์ธก์ ํด์ผ ํ๋์ง์ ๋ํด ์์ง ํ์ต์ด ํ์ํฉ๋๋ค.
๊ทธ๋์, ๊ณต์์ ์ฌ์ฉํ์ง ์๊ณ ๊ฐ๋จํ๊ฒ coreSize ๋ฅผ 2๋ฐฐ๋ก ์ค์ ํ์ต๋๋ค.
-- ์ถ์ฒ: ๋ชจ๋ ์๋ฐ ์ธ ์ก์
์ฌ์ฉ ๊ฐ๋ฅํ ์ฝ์ด ์ * 0๊ณผ 1 ์ฌ์ด์ ๊ฐ์ ๊ฐ๋ CPUํ์ฉ ๋น์จ * (1+ ๋๊ธฐ์๊ฐ/๊ณ์ฐ์๊ฐ)
ThreadPoolTaskExecutor์ BlockingQueue
ThreadPoolTaskExecutor์ ํ๋ ์ด๋ค BlockingQueue ๋ก ๊ตฌํ๋์ด์๋์ง ์์๋ดค์ต๋๋ค.
์์๋ฉด LinkedBlockingQueue, ๋ค๋ฅธ ๊ฐ์ SynchronousQueue ๋ก ๊ตฌํ๋จ์ ์ ์ ์์์ต๋๋ค.
ํด๋น ๊ตฌํ์ฒด ๋ฐ ์ฃผ์ BlockingQueue ๊ตฌํ์ฒด์ ๋ํด ๊ฐ๋จํ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
- LinkedBlockingQueue
- ํฌ๊ธฐ๊ฐ ๋ฌดํํ๊ฑฐ๋ ๊ณ ์ ๋ FIFO ํํ์ ํ ๋๋ค.
- SynchronousQueue
- ๋ฐ์ดํฐ๋ฅผ ํ์ ์ ์ฅํ์ง ์๋๋ค. ์์ฐ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๋ฉด ์๋น์๊ฐ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ๋๊น์ง ๋๊ธฐํ๋ค. ์ฆ, ์ค๊ฐ์ ํ ์์ด ์์ฐ์์ ์๋น์๊ฐ์ ์ง์ ๊ฑฐ๋ํ๋ ๋ฉ์ปค๋์ฆ.
- ArrayBlockingQueue
- ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋ ๋ธ๋กํน ํ๋ก ๊ณต์ (fair)๋ชจ๋๋ฅผ ์ฌ์ฉํ ์ ์์ง๋ง, ์ฑ๋ฅ์ด ์ ํ๋ ์ ์๋ค.
- ๊ณต์ ๋ชจ๋: ํน์ ํ์ ์๋ ์์ ์ ๋จผ์ ๋๊ธฐํ๊ณ ์ฌ๋ฌ ์ค๋ ๋๋ค ์ค ํน์ ์ค๋ ๋๋ก ํ์ฌ๊ธ ์ฒ๋ฆฌํ๊ฒ ํ ์ ์๋ค๋ ๊ฒ ํน์ง์ด๋ค.
- PrioirtyBlockingQueue
- ์ฐ์ ์์๊ฐ ๋์ ์์๋ฅผ ๋จผ์ ์ฒ๋ฆฌํฉ๋๋ค. (๊ทธ ์ด์์ ์ค๋ช ์ Javadoc์ ๋ด์ผํฉ๋๋ค..)
- DelayQueue
- ๊ฐ ์์๋ ์ง์ ๋ ์ง์ฐ ์๊ฐ์ด ์ง๋ ํ์ ์๋น๋ ์ ์๋ค. ์ฆ, ์ผ์ ์๊ฐ์ด ์ง๋ ํ ์์ ์ ์ฒ๋ฆฌํด์ผ ํ๋ ์ค์ผ์ค๋ง ์์ ์ ์ฌ์ฉ๋ฉ๋๋ค.
์ ๊ฐ์ ๊ฒฝ์ฐ๋ ๊ณต์ ๋ชจ๋, ์ฐ์ ์์, ์ง์ฐ ์์๋ฅผ ๊ณ ๋ คํ ํ์๊ฐ ์๋ค๊ณ ํ๋จํด์ ThreadPoolTaskExecutor์ ๊ธฐ๋ณธ ๊ตฌํ์ธ LinkedBlockingQueue ์ ๊ทธ๋๋ก ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์ผ๋ฉฐ ํ์ ์ฌ์ด์ฆ๋ฅผ ์์์ ๊ฐ์ธ 100์ผ๋ก ๋์์ต๋๋ค.
๋ค๋ง, ํ์ ์ฉ๋์ ์ ํ์ ๋๋ค๋ฉด ํ์ ์ฉ๋์ด ๊ฐ๋ ์ฐผ์ ๋์ ์์ ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฒ์ธ์ง ๊ฑฐ์ ์ ์ฑ ์ ์ ํด์ผ ํฉ๋๋ค.
CallerRunsPolicy ์ ํตํด ์์ ์ ์ถ ์ค๋ ๋๋ฅผ ํ์ฉํ๊ธฐ
๊ฑฐ์ ์ ์ฑ ์ ์์ ์ด ์ค๋ ๋ ํ์ ์ต๋ ํฌ๊ธฐ์ ๋๋ฌํ๊ณ ํ๊ฐ ๊ฐ๋ ์ฐผ์ ๋ ์ ์ฉ๋ฉ๋๋ค.
- AbortPolicy: ๊ธฐ๋ณธ ๊ฑฐ์ ์ ์ฑ ์ผ๋ก, ์๋ก์ด ์์ ์ ์ ์ถํ ๋, RejectedExecutionException ์ ๋ฐ์ํ๋ฉฐ ๊ฑฐ์ ํฉ๋๋ค.
- DiscardPolicy: ์๋ก์ด ์์ ์ ์กฐ์ฉํ ๋ฒ๋ฆฝ๋๋ค.
- CallerRunPolicy: ์๋ก์ด ์์ ์ ์ ์ถํ ์ค๋ ๋๊ฐ ๋์ ํด์ ์ง์ ์์ ์ ์คํํฉ๋๋ค.
- ์ฌ์ฉ์ ์ ์(RejectedExecutionHandler): ๊ฐ๋ฐ์๊ฐ ์ง์ ์ ์ํ ๋ก์ง์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
์ ๋ CallerRunsPolicy๋ฅผ ์ ํํ์ต๋๋ค. ๊ทธ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1. ํธ๋ํฝ์ด ๋ง์ ์ํฉ์์์ ์ต์ํ์ ์์ ์ฒ๋ฆฌ
- ์์ ์ ์ถ์ด ๊ฑฐ๋ถ๋๋ ์ํฉ์ ๋์ฒด๋ก ํธ๋ํฝ์ด ๊ณผ๋ํ๊ฒ ๋ง์ ์ํฉ์ผ ๊ฒ์ด๋ผ ์์ํ์ต๋๋ค. ์ด ๋, CallerRunsPolicy ๋ฅผ ํตํด ์์ ์ ์ ์ถํ ์ค๋ ๋์์ ์์ ์ ์ฒ๋ฆฌํ๋ฉด ์ต์ํ์ ์์ ์ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋ฉ๋๋ค.
2. ์ด์ฐจํผ join() ๊ณผ allOf() ๋ก ์ธํด ์์ ์ ์ถ ์ค๋ ๋๊ฐ ๋๊ธฐํฉ๋๋ค.
- ์์ง ์ค๋ช ์ ์๋์์ง๋ง, ์ ๋ @Async ๋์ CompletableFuture ๋ฅผ ํ์ฉํด 7๋ฒ์ ์ธ๋ถ API ํธ์ถ์ ๋น๋๊ธฐ ์์ ์ผ๋ก ์ฒ๋ฆฌํ ํ, join() ์ ํตํด ์์ ์ ์ถ ์ค๋ ๋๊ฐ ๋๊ธฐํ๋ ๊ตฌ์กฐ์ ๋๋ค.
- ์ด์ฐจํผ ํธ์ถ ์ค๋ ๋๊ฐ ๋๊ธฐ ์ค์ด๋ผ๋ฉด, CallerRunsPolicy ๋ฅผ ์ฌ์ฉํด ํธ์ถ ์ค๋ ๋๊ฐ ์ธ๋ถ API ํธ์ถ ์์ ์ ๋์ ์ฒ๋ฆฌํ๋๋ก ํ๋ ๊ฒ์ด ์ ์ ํ๋ค๊ณ ํ๋จํ์ต๋๋ค
๋ค๋ง, ์ด๋ฌํ ๊ณผ์ ์ ๋ง์น ํ๊ฐ ๊ฝ ์ฐจ๋๋ผ๋ ํธ์ถ ์ค๋ ๋๊ฐ ์์ ์ ์คํํ๋ฉด์, ์์คํ ์ด ์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์์ต๋๋ค. ์ด๋ก ์ธํด ๊ฐ๋ฐ์๊ฐ ๊ณผ๋ํ ํธ๋ํฝ ๋ฌธ์ ๋ฅผ ์ฆ๊ฐ ์ธ์งํ๊ธฐ ์ด๋ ค์ธ ์ ์์ต๋๋ค.
์ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด, ๋ชจ๋ํฐ๋ง ์๋ฒ์ ํธ๋ํฝ ์ํ๋ฅผ ํ์ธํ ์ ์๋ ์๋ฆผ ์์คํ ์ ์ถ๊ฐํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ฝ๋ ์ค๋ช : CompletableFuture๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ
์ํ์ค ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ๋ํ๋ธ ์ ์ฒด ํ๋ฆ๋์ ๋๋ค.
3๋ฒ์ด CompletableFuture ๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ด๋ฉฐ, 3๋ฒ๊ณผ 4๋ฒ์ ๋ํด ์ค๋ช ํ๊ฒ ์ต๋๋ค.
์ํ์ค ๋ค์ด์ด๊ทธ๋จ์ 3๋ฒ์ ์๋ ์ฝ๋ 3๋ฒ์ ํด๋นํฉ๋๋ค.
// FACADE ๊ณ์ธต์ ์ฝ๋ ์ค ์ผ๋ถ
public ์๋ต๊ฐ createWeeklyReport(UUID userId, LocalDate startDate) {
1. ์ฃผ๊ฐ ๋ฆฌํฌํธ ์์ฑ ์ฌ๋ถ ํ์ธ
2. ํด๋ก๋ฐ์๊ฒ ์์ฒญ์ ๋ณด๋ผ 1์ฃผ์ผ์น ํธ์ง ์กฐํ
3. ์ผ์ผ ๋ฆฌํฌํธ๋ฅผ ๋ง๋ค ํธ์ง๋ค์ Clova์๊ฒ ์ ์ก <-- ์ธ๋ถ API ์ฌ์ฉ ๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ
4. ํด๋ก๋ฐ๋ก๋ถํฐ ๋ฐ์ ์ผ์ผ ๋ฆฌํฌํธ์ ๋ถ์๊ฒฐ๊ณผ๋ค์ ์ ์ฅ <-- ํธ๋์ญ์
์์ ๋ฐ ์ข
๋ฃ
5. ์ฃผ๊ฐ ๋ถ์์ ํ์ํ ์๋ก ํ๋ง๋ ์กฐํ
6. ์ฃผ๊ฐ ๋ฆฌํฌํธ ์์ฒญ <-- ์ธ๋ถ API ์ฌ์ฉ
7. ์ฃผ๊ฐ ๋ฆฌํฌํธ ์ ์ฅ <-- ํธ๋์ญ์
์์ ๋ฐ ์ข
๋ฃ
return ์ฃผ๊ฐ ๋ฆฌํฌํธ ์ ์ฅ ํ ๋ฐํ๋ฐ์ ์๋ต๊ฐ;
}
์ธ๋ถ API ํธ์ถ์ ๋ด๋นํ๋ Clova ์๋น์ค์์๋ CompletableFuture.supplyAsync() ๋ฅผ ํตํด ๋น๋๊ธฐ๋ก ์ธ๋ถ API๋ฅผ ํธ์ถํ์ผ๋ฉฐ, ๋น๋๊ธฐ์ ํ์ํ ์ฐ๋ ๋๋ ์๋ ๋น์ผ๋ก ๋ฑ๋กํ ์ฐ๋ ๋ ํ์ ์ฌ์ฉํ๋๋ก ๊ตฌํํ์ต๋๋ค.
public class ClovaService {
private final Executor clovaExecutor;
public CompletableFuture<ClovaResponseDto> sendAsyncDailyReportRequest(...) {
return CompletableFuture.supplyAsync(() -> {
...๋ก์ง ์๋ต
return ์ธ๋ถ API ํธ์ถ;
}, clovaExecutor);
}
์ด๋ ๋ฏ ์ธ๋ถ API ํธ์ถ์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด CompletableFuture๋ฅผ ํ์ฉํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ , thenApply ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ต ๊ฐ์ ๋ํ ์ฝ๋ฐฑ์ ์ถ๊ฐํ์ต๋๋ค.
// 3๋ฒ ๋ก์ง ์ค, ์ผ๋ถ
Map<CompletableFuture<ํค>, List<๊ฐ>> futures = Map.values().stream()
.collect(Collectors.toMap(
๊ฐ -> clovaService.sendAsyncDailyReportRequest(letters)
.thenApply(~~),
๊ฐ -> ๊ฐ
));
โถ ์ธ๋ถ API ๊ฒฐ๊ณผ ์ ์ฅํ๋ ๋ก์ง์ ๋น๋๊ธฐ ์์ ํ ์ฝ๋ฐฑ์ ์ถ๊ฐํ์ง ์์ ์ด์
์ธ๋ถ API ํธ์ถ์ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ ์์ ์ ์ฝ๋ฐฑ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์์ง๋ง, ๊ทธ๋ ๊ฒ ๋๋ฉด save ์ฟผ๋ฆฌ๊ฐ ์ต๋ 7๋ฒ ๋ฐ์ํ๊ฒ ๋์ด saveAll๋ก ์ฒ๋ฆฌํ๋ ๊ฒ๋ณด๋ค ์๋๊ฐ ๋๋ ค์ก์ต๋๋ค.
๋ฐ๋ผ์, saveAll์ ์ฌ์ฉํ๊ธฐ ์ํด ๋น๋๊ธฐ ๊ฒฐ๊ณผ๊ฐ ๋ชจ๋ ์๋ฃ๋ ๋๊น์ง join() ์ด๋ allOf()๋ก ๊ธฐ๋ค๋ฆฐ ํ ํ ๋ฒ์ ์ ์ฅํ๋๋ก ๊ตฌํํ์ต๋๋ค.
โถ @Async ์ด๋ ธํ ์ด์ ์ ํ์ฉํ์ง ์์ ์ด์ ?
@Async๋ ๊ธฐ๋ณธ์ ์ผ๋ก Async-nonBlocking ๋ฐฉ์์ผ๋ก ๋์ํ์ฌ ์ฝ๋ฌ ์ค๋ ๋์๊ฒ ์ ์ด๊ถ์ ์ฆ์ ๋๊ฒจ์ ์ฝ๋ฌ๊ฐ ๋ค์ ๋ก์ง์ ์คํํ๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง ์ ๋ saveAll() ์ ์ํด join() ์ฒ๋ฆฌ(Blocking)๊ฐ ํ์ํ์ต๋๋ค. ๋ฐ๋ผ์ ๋ฐํ ํ์ ์ CompletableFuture๋ก ํด์ผ ํ์ต๋๋ค. ๋ง์ฝ, @Async ์ด๋ ธํ ์ด์ ์ ํ์ฉํ๋ค๋ฉด CompletableFuture.supplyAsync() ๊ฐ ์๋ ์ฝ๋์ฒ๋ผ CompletableFuture.completedFuture() ๋ก ๋ณํ ๊ฒ์ ๋๋ค.
์ด๋ ๊ฒ ๋ ๊ฒฝ์ฐ, ๋น๋๊ธฐ๋ฅผ ๋ด๋นํ๋ CompletableFuture๊ณผ @Async ์ ์ญํ ์ด ์ค๋ณต๋๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ @Async๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ๋ก ํ์ต๋๋ค.
public class ClovaService {
@Async("clovaExecutor")
public CompletableFuture<ClovaResponseDto> sendAsyncDailyReportRequest(...) {
...๋ก์ง ์๋ต
return CompletableFuture.completedFuture(์ธ๋ถ API ํธ์ถ);
}
โถ CompletableFuture ๋ฅผ ์ฌ์ฉํ๋๋ฐ ForkJoinPool ๋์ ThreadPoolTaskExecutor ์ ์ฌ์ฉํ ์ด์
CompletableFuture๋ ๊ธฐ๋ณธ์ ์ผ๋ก ForkJoinPool ์ ์ฐ๋ ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ทธ๋์, ์ฐ๋ ๋ ํ์ ์ค์ ํ์ง ์๋๋ผ๋ ๋น๋๊ธฐ๋ก ์์ ์ ์ํํ ์ ์์ต๋๋ค. ํ์ง๋ง ForkJoinPool์ ์ฐ๋ ๋๋ ๋ฐ๋ชฌ ์ฐ๋ ๋๋ก, I/O ์์ ์ด ์๋ฃ๋์ง ์๋๋ผ๋ JVM์ด ์ข ๋ฃ๋ ์ ์์ต๋๋ค. ์ฆ, I/O ์์ ์ค ์๋ฒ๊ฐ ์ข ๋ฃ๋๊ณ ์ฐ์ํ ์ข ๋ฃ๋ ๋ถ๊ฐํ๊ฒ ๋๋ฌธ์ ๋๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ThreadPoolTaskExecutor๋ฅผ ์ฌ์ฉํ์ฌ, CompletableFuture ์์ ์ด ๋ฐ๋ชฌ ์ฐ๋ ๋๊ฐ ์๋ ์ฌ์ฉ์ ์ฐ๋ ๋์์ ์คํ๋๋๋ก ์ค์ ํ์ต๋๋ค
* ์ด์ ๋ํด ์ ๋ฆฌํ ์ ๊ธ: ์ ๋ด CompletableFuture ๋ Console ์ ๋ก๊ทธ๊ฐ ์๋๊ฐ? ForkJoinPool ์ ๋ฐ๋ชฌ ์ค๋ ๋๋ฅผ ์์๋ณด์
โถ I/O ์์ ์ ๋น๋๊ธฐ๋ก ์์ ์, ํ์์์ ์ค์ ์ ์ด๋ป๊ฒ ํ๋์ง?
๋จผ์ allOf()๋ฅผ ์ฌ์ฉํ์ง ์์ ์ด์ ๋ Map์ ์ฌ์ฉํ๊ฒ ๋๋ฉด์ ์ฝ๋๊ฐ join() ๋ณด๋ค ๋ณต์กํด์ก๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋์, join()์ stream() ๋ด๋ถ์์ join()์ ํ์ฉํด ๋น๋๊ธฐ ์์ ์ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌํ๋๋ก ๊ตฌํํ์ต๋๋ค.
๊ทธ๋ฌ๋, stream()์ ๋ณธ์ง์ ์ผ๋ก ์์ฐจ์ ์ผ๋ก ์ฐ์ฐ์ด ์งํ๋๋ฏ๋ก, ์์ ์์ ์ด ์๋ฃ๋์ง ์์ผ๋ฉด ๋ค์ ์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด CompletableFuture ๋ ๋น๋๊ธฐ ์ฐ์ฐ์ด ์ง์ ๋ ์๊ฐ์์ ๋๋์ผํ๋ orTimeout() ๋ฉ์๋๋ฅผ ์ง์ํฉ๋๋ค.
ํ์ง๋ง, ์ ๊ฒฝ์ฐ ๋น๋๊ธฐ ์์ ์ ์ง์ฐ ๊ฐ๋ฅ์ฑ์ด ์ธ๋ถ API ํธ์ถ์ ํ์ ๋์๊ธฐ ๋๋ฌธ์ FeignClient ์ timeout ์ 10์ด๋ก ์ค์ ํ์ฌ stream() ์ ์์ฐจ์ ์ธ ๋ฌธ์ ๋ฅผ ๋ณด์ํ์ต๋๋ค.
โถ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ค ์์ธ ์ฒ๋ฆฌ๋ ์ด๋ป๊ฒ?
CompletableFuture์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด, exceptionally()๋ฅผ ํตํด ์์ธ ๋ฐ์ ์ ์ํํ ํ๋์ ์ฝ๋ฐฑ์ผ๋ก ๋ฑ๋กํ์ฌ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํ๊ฑฐ๋ ๋ค๋ฅธ ์์ธ๋ฅผ ๋์ง ์ ์์ต๋๋ค.
์ ๋ join()์ ์ฌ์ฉํ๊ณ , join() ํธ์ถ ์ ๋น๋๊ธฐ ์์
์ค ์์ธ๊ฐ ๋ฐ์ํ๋ฉด CompletionException ์์ธ๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด ์์ธ๋ฅผ @RestControllerAdvice๋ฅผ ํ์ฉํ์ฌ ์ฒ๋ฆฌํ์๊ณ , ๋ค๋ฅธ ์์ธ๋ค๊ณผ ํจ๊ป ๋ชจ๋ํํ์ต๋๋ค.
๊ฒฐ๊ณผ: 1๋ถ → 15์ด
๋ก์ปฌ ์๋ฒ (๋ ผ๋ฆฌ ์ฝ์ด 16, ๋ฉ๋ชจ๋ฆฌ 16)
- API ์ ์ฒด ์๋ต ์๊ฐ
- ๊ธฐ์กด ๋๊ธฐ ๋ฐฉ์: ์ฝ 1๋ถ
- ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ: ์ฝ 14์ด
QA ์ ์ฌ์ฉํ๋ ์๋ฒ ์คํ์ ์ฝ์ด 2, ๋ฉ๋ชจ๋ฆฌ 8 ์ด์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ , ๋๊ธฐ ๋ฐฉ์์ API ์ ์ฒด ์๋ต ์๊ฐ์ ์ฌ์ฉ์๋น 1๋ถ 30์ด ~ 2๋ถ ์ด์์ผ๋ก ๊ฑธ๋ ธ๋๋ฐ, QA๋ฅผ ์งํํ๊ณ ์ถํ์ ์ ๋ฐ์ดํธ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
๋ง์น๋ฉฐ
์ ๋ ์ด์ ์๋ @Async ๋ฅผ ํ์ฉํด ์ด๋ฉ์ผ ์ ์ก์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ๊ฒฝํ์ด ์์์ต๋๋ค. ํ์ง๋ง, ๊ทธ ๋์ ๋ค๋ฅธ์ ์ ๊ณ ๊ธ ์๋ฐ ๊ฐ์๋ฅผ ์๊ฐ์ ํ๊ธฐ ์ ๊ณผ ํ๋ก, ํ์ฌ๋ ์ฐ๋ ๋ ํ์ ๋ฐ๋ผ๋ณด๋ ๋ฐ๋ผ๋ณด๋ ์๊ฐ์ด ์กฐ๊ธ์ ๋์ด์ ธ์ ๊ธ์ด ๊ธธ์ด์ง๊ณ , ์ข ๋ ๋ค์ํ ์ํฉ์ ๊ณ ๋ คํ ์ ์๊ฒ ๋์ด ์ฆ๊ฒ๊ฒ ๊ฐ์ ํ ๊ฒ ๊ฐ์ต๋๋ค. ๋ํ, ์ฝ๋์ค์ฟผ๋ ๊ต์ก ๋น์ ๋น๋๊ธฐ๋ก ์ฝ์์ CompletableFuture ๋ฅผ ํ์ฉํด ์ฝ์์ ๋น๋๊ธฐ๋ก ์นดํํ ๋ฆฌ์๋ฅผ ๊ตฌํํ ๊ฒฝํ์ด ๋์์ด ๋์ด์ ๋คํ์ด์์ต๋๋ค. ๊ทธ๋ฌ๋, ์์ง CompletableFuture ๊ฐ ์ฐ๋ ๋ ํ์์ ์ด๋ค ์๋ฆฌ๋ก join() ํ๋์ง, ์ด๋ป๊ฒ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋์ง ์ฝ๋ ๋ ๋ฒจ์ ๊ฒฝํ์ด ๋ถ์กฑํฉ๋๋ค. ์์ผ๋ก๋ ๋น๋๊ธฐ๋ฅผ ๊ณต๋ถํ๋ฉฐ ๋ณด์ํด ๋๊ฐ๋ณด๋ ค ํฉ๋๋ค.
์ด๋ฒ ํ๋ก์ ํธ์์๋ ๋น๋๊ธฐ๋ก ์ฑ๋ฅ์ ๊ฐ์ ํ์ง๋ง, Non-Blocking I/O (WebClient) ๋ฐฉ์์ ํ์ฉํด ์๋ต ์๊ฐ์ ์ค์ผ ์ ์๋ค๊ณ ์๊ฐ์ด ๋ค๊ธฐ๋ ํฉ๋๋ค. ๊ทธ๋์, ์ถํ WebClient ๋ฅผ ํ์ตํ๊ณ ์ ์ฉํด ๋ณผ ๊ณํ์ ๋๋ค.
๋์ผ๋ก, ๊ธ์ ์ฝ์ผ์๋ฉด์ ๋ถ์กฑํ ๋ถ๋ถ์ด๋ ๊ฐ์ ์ด ํ์ํ ์ ์ด ์๋ค๋ฉด ํผ๋๋ฐฑ ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
@Async ๋ฐ CompletableFuture ํ์ฉ์ ๋์ ์ฃผ์๋ ์ฐธ๊ณ ์๋ฃ๋ค
- ๋ชจ๋ ์๋ฐ ์ธ ์ก์
- ๊น์ํ๋ ๊ณ ๊ธ ์๋ฐ ๊ฐ์
- [Spring] @async ๋ก์ง ์คํจ ์ผ๋๊ธฐ, ThreadPoolTaskExecutor์ awaitTerminate์ Async
- [Youtube] ๊ฐ๋ฐ์ ์ฅ๊ณ ๋ - ์๋ฐ ๋น๋๊ธฐ ๋ฐ๋ผํ๊ธฐ 4, Spring Async
- Graceful Shutdown in Spring Boot with Sync and Async Tasks
- ThreadPoolTaskExecutor์ waitForTasksToCompleteOnShutdown ์์ฑ ์์๋ณด๊ธฐ
- ์ฐ๋ ๋ ํ๊ณผ ForkJoinPool
- [Java] CompletableFuture ์ฌ์ฉ๋ฒ
- ์ฌ๋ฅ๋ท: Spring @Async์ CompletableFuture๋ฅผ ์ด์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ์ธ๊ณ๋ก!
- f-lab: ์คํ๋ง๊ณผ CompletableFuture๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ