
๐ฆ 3ํธ. ThreadPool & Tomcat ํ๋ — ํธ๋ํฝ ํญ์ฃผ ์ ์๋ฒ๊ฐ ๋ฒํฐ์ง ๋ชปํ๋ ์ง์ง ์ด์
์ด์ ์๋น์ค์์ ํธ๋ํฝ์ด ๊ฐ์๊ธฐ ๋ชฐ๋ฆฌ๋ฉด ๊ฐ์ฅ ๋จผ์ ๋ฌธ์ ๊ฐ ํฐ์ง๋ ๊ณณ์ด ๋ฐ๋ก ThreadPool์ด๋ค.
DB๋ ์บ์๊ฐ ์๋ฌด๋ฆฌ ๋นจ๋ผ๋, Tomcat ์ค๋ ๋๊ฐ ๋ฐ๋ฅ๋๋ฉด ์ ์ฒด ์๋น์ค๊ฐ ๋ฉ์ถฐ ๋ฒ๋ฆฐ๋ค.
ํนํ ๊ธ์ต๊ถ์ฒ๋ผ ํน์ ์๊ฐ(์ค์ 9์, ์ ์ฌ ์ง์ )์ ํธ๋ํฝ์ด ํญ๋ฐํ๋ ์๋น์ค์์๋
ThreadPool ํ๋์ด ์ฅ์ ์๋ฐฉ์ ํต์ฌ์ด๋ค.
์ด๋ฒ ํธ์์๋ ์ค์ ์ด์ ํ๊ฒฝ์์ ๊ฒช์๋ Tomcat ์ค๋ ๋ ํ ๊ณ ๊ฐ → API ์ ๋ฐ ํ์์์ ๋ฌธ์ ๋ฅผ ๊ธฐ์ค์ผ๋ก
์ด๋ป๊ฒ ์์ ํ ํ๋์ง ์์ธํ ์ ๋ฆฌํด๋ณธ๋ค.
โ ๋ฌธ์ 3: Tomcat ์ค๋ ๋ ํ ๊ณ ๊ฐ๋ก ์ ์ฒด API ์ง์ฐ
ํน์ ์ผ์ ์ค์ 9์, ๋ชจ๋ API ์๋ต์ด ๊ฐ์๊ธฐ 5~10์ด๋ก ์น์๋ ํ์์ด ๋ฐ์ํ๋ค.
APM๊ณผ ์๋ฒ ๋ก๊ทธ๋ฅผ ํ์ธํด๋ณด๋ ๊ณตํต์ ์ธ ์์ธ์ ๋ค์๊ณผ ๊ฐ์๋ค.
- Tomcat max-threads ๊ณ ๊ฐ
- ์์ฒญ์ด ํ์ ์์ด๋ฉฐ ๋๊ธฐ ์๊ฐ๋ง 4์ด ์ด์ ๋ฐ์
- ์ผ๋ถ ์์ฒญ์ ๊ฒฐ๊ตญ 503 Service Unavailable ๋ฐ์
- ๋ด๋ถ API ํธ์ถ๊น์ง ์ค์ค์ด ํ์์์
๋ฌธ์ ์ ์์ธ์ ์ถ์ ํ๊ธฐ ์ํด Thread Dump๋ฅผ ์ดํด๋ณด๋
๋๋ถ๋ถ์ Request Thread๊ฐ ์ธ๋ถ API ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ฉฐ BLOCKED ์ํ์ ์์๋ค.
์ฆ, “CPU๊ฐ ๋ถ์กฑํ ๊ฒ ์๋๋ผ ์ค๋ ๋๊ฐ ๋ถ์กฑํ ์ํฉ”์ด์๋ค.
โ ์์ธ ๋ถ์
1) Tomcat ๊ธฐ๋ณธ ์ค๋ ๋ ์๊ฐ ์ ๋์ ์ผ๋ก ๋ถ์กฑ
Spring Boot ๋ด์ฅ Tomcat ๊ธฐ๋ณธ๊ฐ:
max-threads = 200
๋๋ ํธ๋ํฝ์ด ๋ชฐ๋ฆฌ๋ฉด 200์ ๊ธ๋ฐฉ ๋ฐ๋ฅ๋๋ค.
2) slow API ํ๋๊ฐ ์ ์ฒด ์์คํ ์ ๋ฉ์ถค
N๊ฐ์ ์์ฒญ ์ค ๋จ 1๊ฐ์ API๊ฐ 3์ด๋ง ๊ฑธ๋ ค๋
๊ทธ ์ค๋ ๋๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ์์ฒญ์ด ์ง์ฐ๋๋ค.
3) ๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ฐ๋ฅํ ๋ก์ง๋ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ
์:
- ์ธ๋ถ ์๋ฆผ ์ ์ก API
- ๋ก๊ทธ ์ ์ฌ API
- ๋ฑ๋ก/์์ ์ดํ ๋ฐฑ์คํผ์ค ์๋ฆผ
์ด๋ฐ ๊ฒ๋ค์ด ๋ชจ๋ Request Thread๋ฅผ ํต๊ณผํ๋ฉด ๊ณ ๊ฐ์ด ๋นจ๋ผ์ง๋ค.
โ ํด๊ฒฐ ๋ฐฉ๋ฒ
์ด์ ํ๊ฒฝ์์ ํจ๊ณผ๊ฐ ์ปธ๋ ์กฐ์น๋ค์ ์์๋๋ก ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
โ 1) Tomcat max-threads ํ๋
server:
tomcat:
max-threads: 400
accept-count: 200
min-spare-threads: 20
ํจ๊ณผ
- ๋์ ์ ์ ์์ฒญ ์ฆ๊ฐ ๊ตฌ๊ฐ์์๋ ์ค๋ ๋ ๊ณ ๊ฐ ๋ฐฉ์ง
- ๋๊ธฐ ์์ฒญ(accept-count) ์ฆ๊ฐ๋ก ์๊ฐ์ ์ธ ํธ๋ํฝ ํญ์ฃผ์๋ ์์ ์ ์ผ๋ก ์ฒ๋ฆฌ
โ 2) ์ธ๋ถ API ํธ์ถ์ ๋ฐ๋์ ๋ณ๋ ThreadPool ์ฌ์ฉ
๋น์ฆ๋์ค ๋ก์ง์์ ์ธ๋ถ API๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ๋ถ๋ฆฌํ๋ค.
@Configuration
public class AsyncConfig {
@Bean(name = "externalApiExecutor")
public Executor externalApiExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("api-executor-");
executor.initialize();
return executor;
}
}
์ฌ์ฉ:
@Async("externalApiExecutor")
public void callExternalApi() {
// ์ธ๋ถ ์๋ฆผ, ์นํ
ํธ์ถ ๋ฑ
}
ํจ๊ณผ
- ์ธ๋ถ API ์๋ต ์ง์ฐ์ด Tomcat Request Thread์ ์ํฅ์ ์ฃผ์ง ์์
- ์ ์ฒด API ์๋ต ์๋ ์์ ํ
โ 3) CompletableFuture ๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌ
๋๋ ์กฐํ ํ ์ฌ๋ฌ ๋ถ๊ฐ ์ ๋ณด๋ฅผ ์กฐํฉํด์ผ ํ ๋,
๋๊ธฐ ํธ์ถ์ ๋ณ๋ ฌ๋ก ์ ํํ๋ฉด ์๋ต ์๊ฐ์ด ์ ๋ฐ ์ดํ๋ก ์ค์ด๋ ๋ค.
CompletableFuture<DataA> a = serviceA.getData();
CompletableFuture<DataB> b = serviceB.getData();
CompletableFuture.allOf(a, b).join();
return new Response(a.get(), b.get());
ํจ๊ณผ
- ์ค๋ ๋๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์ฉ → TPS 15~30% ์ฆ๊ฐ
- ์ธ๋ถ API ์กฐํฉํ ์๋ต์ ์ฑ๋ฅ ๊ฐ์
โ 4) Slow Request Trace ๋ก ์ค๋ ๊ฑธ๋ฆฌ๋ API ์ ์ ํ์
์ค๋ ๋๊ฐ “์ ์ค๋ ์ ์ ๋๋์ง”๋ฅผ ๋ชจ๋ฅด๋ฉด ํ๋์ ์๋ฏธ๊ฐ ์๋ค.
์ด์์์๋ 300~400ms ์ด์ ๊ฑธ๋ฆฌ๋ ์์ฒญ๋ง ํํฐ๋งํ๋ ๊ฒ์ด ํจ๊ณผ์ ์ด๋ค.
Spring Boot Filter ๋ฑ๋ก:
@Component
public class SlowRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long start = System.currentTimeMillis();
chain.doFilter(request, response);
long end = System.currentTimeMillis();
if (end - start > 300) {
log.warn("[SLOW] {}ms | URI={}", (end - start),
((HttpServletRequest) request).getRequestURI());
}
}
}
ํจ๊ณผ
- “์ค๋ ๋๋ฅผ ์ก์๋จน๋ API”๋ฅผ ์ ํํ ์ฐพ์ ์ฆ์ ํ๋ ๊ฐ๋ฅ
โ ์ ์ฉ ํ ์ฑ๋ฅ ๊ฐ์ ํจ๊ณผ
์ด์ ์๋ฒ ๊ธฐ์ค:
ํญ๋ชฉ ๊ฐ์ ์ ๊ฐ์ ํ
| Tomcat ์ค๋ ๋ ๊ณ ๊ฐ | ์์ฃผ ๋ฐ์ | ๊ฑฐ์ ์์ |
| ์๊ฐ TPS | 2,000 ์ดํ | 4,000 ์ด์ |
| ์ต๋ ์๋ต ์๊ฐ | 10์ด ์ด์ | 300ms ์ด๋ด |
| Error Rate | 4%↑ | 0.1% ์ดํ |
ํนํ ํผํฌ ์๊ฐ(์ค์ 9์)์ API๊ฐ ์ ๋ถ ๋ฉ์ถ๋ ์ฅ์ ๊ฐ ์์ ํ ์ฌ๋ผ์ก๋ค.
โ ์ค์ ํ 3๊ฐ
1. max-threads ๋ฅผ ๋๋ฆฐ๋ค๊ณ ๋ชจ๋ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋๋ ๊ฑด ์๋๋ค
์ค๋ ๋๋ฅผ ์ ๊ฐ๋ฒ๋ฆฌ๋ ๊ทผ๋ณธ ์์ธ(์ธ๋ถ API·DB ์ง์ฐ)์ ๋จผ์ ํด๊ฒฐํด์ผ ํ๋ค.
2. ์ธ๋ถ API ํธ์ถ์ ๋ฐ๋์ ๋ณ๋ ThreadPool ๋ก ๋ถ๋ฆฌํ๋ผ
๊ฐ์ฅ ํฐ ์ฒด๊ฐ ์ฑ๋ฅ ํฅ์์ ์ฃผ๋ ํ๋์ด๋ค.
3. “300ms ์ด์ ๊ฑธ๋ฆฌ๋ ์์ฒญ”๋ง ๊ณ์ ์ถ์ ํ๋ผ
slow request ๋ฅผ ์ถ์ ํ๋ฉด ๋๋ถ๋ถ์ ๋ณ๋ชฉ์ด ๋์ ๋ณด์ธ๋ค.
๐งฉ ๋ง๋ฌด๋ฆฌ
ThreadPool ํ๋์ ๋จ์ํ ์๋ฒ ์คํ์ ๊ฐํํ๋ ์์
์ด ์๋๋ค.
์ด์ ํ๊ฒฝ์์๋ ์์ ์ง์ฐ์ด ์ ์ฒด ์์คํ
์ ๋ฉ์ถ๊ฒ ๋ง๋ค ์ ์๋ค.
์ด๋ฒ ํธ์์๋ ์ค์ ๋ก ์ด์ ์ฅ์ ๋ฅผ ํด๊ฒฐํ๋
Tomcat ์ค๋ ๋ ํ๋ + ๋น๋๊ธฐ ๋ถ๋ฆฌ + ๋ณ๋ ฌ ์ฒ๋ฆฌ ๊ตฌ์กฐ ๊ฐํธ์ ๋ค๋ค๋ค.
๋ค์ ํธ์์๋ DB ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋ฐฉ์,
๊ทธ๋ฆฌ๊ณ ์ค์ ์ด์์์ ํจ๊ณผ๊ฐ ์ปธ๋ SQL ํ๋ ์ฌ๋ก๋ฅผ ๋ค๋ฃฐ ์์ ์ด๋ค.
๐ TAGS
Java, SpringBoot, Tomcat, ThreadPool, Async, PerformanceTuning, BackendOptimization, HighTraffic, ProductionIssue, RealWorldTips
- 1ํธ — ์ด์ ์๋ฒ์์ ์ค์ ๋ก ํจ๊ณผ ๋ณธ ๋ฐฉ๋ฒ
- 2ํธ — Jackson ์ง๋ ฌํ ํ๋: ์จ์ CPU ๋ณ๋ชฉ ํด๊ฒฐ
- 3ํธ — ThreadPool & Tomcat ํ๋: ํธ๋ํฝ ํญ์ฃผ ๋์ (ํ์ฌ ๊ธ)
- 4ํธ — DB ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง๊ณผ SQL ํ๋ ์ฌ๋ก
- 5ํธ — ์ด์ ์ฅ์ ์ฌ๋ก 10์ + ์ฅ์ ๋ณต๊ตฌ ํ๋ ์ด๋ถ