๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Java & Spring ์‹ค์ „ ๊ฐœ๋ฐœ

3ํŽธ. ThreadPool & Tomcat ํŠœ๋‹ — ํŠธ๋ž˜ํ”ฝ ํญ์ฃผ ์‹œ ์„œ๋ฒ„๊ฐ€ ๋ฒ„ํ‹ฐ์ง€ ๋ชปํ•˜๋Š” ์ง„์งœ ์ด์œ 

๋ฐ˜์‘ํ˜•

 

ThreadPool & Tomcat ํŠœ๋‹

๐ŸŸฆ 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

๋ฐ˜์‘ํ˜•