Spring Boot 의 닀쀑 μœ μ € μš”μ²­ 처리 방법 (Tomcat)

1. Thread

μŠ€λ ˆλ“œμ˜ κ°œλ…μ€ λ‹€μŒκ³Ό κ°™λ‹€.

싀행쀑인 ν•œ ν”„λ‘œκ·Έλž¨(ν”„λ‘œμ„ΈμŠ€) λ‚΄μ—μ„œ ꡬ뢄지어진 μ‹€ν–‰ λ‹¨μœ„

 

ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€μ—μ„œ μž‘μ—…μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ λ‹¨μœ„λ‘œ μƒκ°ν•˜λ©΄ λœλ‹€.

 

μ‹€μƒν™œ 예λ₯Ό λ“€λ©΄, 은행은 μ€ν–‰μ΄λΌλŠ” ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ μ—¬λŸ¬ 고객의 λ™μ‹œ 거래 μš”μ²­ μž‘μ—…μ„ μ²˜λ¦¬ν•΄μ•Όν•œλ‹€.

μ΄λ•Œ 은행이 λ³‘λ ¬μ μœΌλ‘œ λ™μ‹œ 거래 μš”μ²­ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ”λ°, ν•΄λ‹Ή μž‘μ—…μ΄ μ΄λ£¨μ–΄μ§€λŠ” λ‹¨μœ„κ°€ μŠ€λ ˆλ“œμΈ 것이닀.

 

μ»΄ν“¨ν„°μ˜ CPUλŠ” μŠ€λ ˆλ“œ λ‹¨μœ„λ‘œ μž‘μ—…μ„ μ²˜λ¦¬ν•œλ‹€. CPUλŠ” μ—¬λŸ¬ μŠ€λ ˆλ“œλ₯Ό λ²ˆκ°ˆμ•„κ°€λ©° μ‹€ν–‰ν•΄μ„œ 닀쀑 μž‘μ—…μ„ μ²˜λ¦¬ν•œλ‹€. CPUλ₯Ό μ—„μ²­ 짧은 μ‹œκ°„μœΌλ‘œ μ‹œλΆ„ν• ν•΄μ„œ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— 우리 λˆˆμ—λŠ” λ™μ‹œμ— μ²˜λ¦¬λ˜λŠ” κ²ƒμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ, 사싀은 μ—¬λŸ¬ μŠ€λ ˆλ“œλ₯Ό λ²ˆκ°ˆμ•„κ°€λ©΄μ„œ μž‘μ—…μ„ μ‹€ν–‰ν•˜λŠ” 것이닀.

 

 

 

❓ Threadκ°€ λ§ŽμœΌλ©΄ λ§Žμ„μˆ˜λ‘ μ’‹μ„κΉŒ?

1. ThreadλŠ” μƒμ„± μ‹œ λ©”λͺ¨λ¦¬λ₯Ό μ†Œλͺ¨ν•œλ‹€. 

μŠ€λ ˆλ“œκ°€ μƒμ„±λ˜λ©΄, ν•΄λ‹Ή μŠ€λ ˆλ“œλŠ” 싀행에 ν•„μš”ν•œ λ©”λͺ¨λ¦¬λ₯Ό ν• λ‹Ήλ°›λŠ”λ‹€. λ”°λΌμ„œ 생성 μ‹œ λ©”λͺ¨λ¦¬λ₯Ό μ†Œλͺ¨ν•˜κ²Œ λœλ‹€.

μŠ€λ ˆλ“œκ°€ λ§Žμ•„μ§€λ©΄ 각 μŠ€λ ˆλ“œκ°€ μ°¨μ§€ν•˜λŠ” λ©”λͺ¨λ¦¬κ°€ μ»€μ Έμ„œ λ©”λͺ¨λ¦¬κ°€ λΆ€μ‘±ν•΄μ§ˆ 수 μžˆλ‹€.

 

 

2. Threadκ°€ λ§Žμ•„μ§€λ©΄ CPU의 μžμ› 경합이 λ°œμƒν•  수 μžˆλ‹€.

μŠ€λ ˆλ“œκ°€ λ§Žμ•„μ§€λ©΄ CPU μžμ›μ„ κ²½ν•©ν•˜λŠ” κ²½μš°κ°€ λ°œμƒν•  수 μžˆλ‹€. ν•˜λ‚˜ μ΄μƒμ˜ μŠ€λ ˆλ“œκ°€ 데이터λ₯Ό κΈ°λ‘ν•˜λ €κ³  ν•  λ•Œ λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ ν•΄λ‹Ή 데이터λ₯Ό 읽으렀고 ν•˜λŠ” 경우λ₯Ό λ§ν•œλ‹€. μ΄λ ‡κ²Œ 경합이 λ°œμƒν•˜λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— μ˜ˆμƒμΉ˜ λͺ»ν•œ λ™μž‘μ΄ λ°œμƒν•  수 μžˆλ‹€.

 

 

3. Threadκ°€ λ§Žμ•„μ§€λ©΄ μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­ λΉ„μš©μ΄ 컀진닀.

μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­μ΄λž€, CPUμ—μ„œ μ‹€ν–‰μ€‘μ΄λ˜ μŠ€λ ˆλ“œκ°€ λ‹€λ₯Έ μŠ€λ ˆλ“œλ‘œ λ°”λ€ŒλŠ” 것을 μ˜λ―Έν•œλ‹€. μŠ€λ ˆλ“œκ°€ λ‹€λ₯Έ μŠ€λ ˆλ“œλ‘œ λ°”λ€Œλ©΄μ„œ, 바뀐 μŠ€λ ˆλ“œμ˜ 정보λ₯Ό λ³€κ²½ν•΄μ•Ό ν•˜λŠ”λ° μ΄λ•Œ CPUκ°€ ν•΄λ‹Ή μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­μ„ μˆ˜ν–‰ν•˜μ—¬ CPU의 μ‹œκ°„μ„ μ†Œλͺ¨ν•œλ‹€.

 

λ”°λΌμ„œ, μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­μ΄ μΌμ–΄λ‚˜λ©΄ CPU의 μ‹œκ°„μ„ μ†Œλͺ¨ν•˜κ²Œ λœλ‹€. μŠ€λ ˆλ“œκ°€ 많으면 λ§Žμ„μˆ˜λ‘ μŠ€λ ˆλ“œ κ°„μ˜ μ „ν™˜μ΄ λΉˆλ²ˆν•΄μ§ˆ 것이고, 이에 λ”°λΌμ„œ μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­ λΉ„μš©μ΄ 컀질 κ²ƒμ΄λ―€λ‘œ CPU의 μ‹œκ°„μ„ μ†Œλͺ¨ν•˜κ²Œ 되고 더 λ‚˜μ•„κ°€μ„œλŠ” CPU에 μ˜€λ²„ν—€λ“œκ°€ λ°œμƒν•˜μ—¬ μ„±λŠ₯이 μ €ν•˜ν•  수 μžˆλ‹€.

 

 

 

 

 

 

 

 

 

 

2. Spring Boot와 Tomcat

μŠ€ν”„λ§κ³Ό μŠ€ν”„λ§λΆ€νŠΈμ˜ μ£Όμš”ν•œ 차이점 쀑 ν•˜λ‚˜λŠ”, μŠ€ν”„λ§ λΆ€νŠΈμ—μ„  λ‚΄μž₯ μ„œλΈ”λ¦Ώ μ»¨ν…Œμ΄λ„ˆ(Tomcat)을 μ§€μ›ν•œλ‹€λŠ” 것이닀. λ”°λΌμ„œ application.yml νŒŒμΌμ— 섀정을 μ£ΌλŠ” κ²ƒλ§ŒμœΌλ‘œ κ°„νŽΈν•˜κ²Œ Tomcat의 섀정을 λ°”κΏ€ 수 μžˆλ‹€. 

 

 

μ•„λž˜λŠ” μ„œλ²„(Tomcat)의 섀정을 λ°”κΏ”μ£ΌλŠ” μ˜ˆμ‹œμ΄λ‹€. 

server:
  tomcat:
    threads:
      max: 200 # 생성할 수 μžˆλŠ” thread의 총 개수
      min-spare: 10 # 항상 ν™œμ„±ν™” λ˜μ–΄μžˆλŠ”(idle) thread의 개수
    max-connections: 8192 # μˆ˜λ¦½κ°€λŠ₯ν•œ connection의 총 개수
    accept-count: 100 # μž‘μ—…νμ˜ μ‚¬μ΄μ¦ˆ
    connection-timeout: 20000 # timeout νŒλ‹¨ κΈ°μ€€ μ‹œκ°„, 20초
  port: 8080 # μ„œλ²„λ₯Ό λ„μšΈ 포트번호

 

λ§Œμ•½ 섀정을 주지 μ•ŠλŠ”λ‹€λ©΄, λ””ν΄νŠΈκ°’μ„ μ£Όμž…ν•˜κ²Œ λœλ‹€. ν•΄λ‹Ή λ””ν΄νŠΈ 값은 ServerProperties ν΄λž˜μŠ€μ—μ„œ 확인할 수 μžˆλ‹€. 

 

 

 

 

 

 

 

 

3. Thread Pool

μŠ€λ ˆλ“œ ν’€μ΄λž€

Thread Pool은 ν”„λ‘œκ·Έλž¨ 싀행에 ν•„μš”ν•œ Thread듀을 미리 μƒμ„±ν•΄λ†“λŠ”λ‹€λŠ” κ°œλ…μ΄λ‹€.(ThreadλŠ” cpu의 μžμ›μ„ μ΄μš©ν•˜μ—¬ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” ν•˜λ‚˜μ˜ λ‹¨μœ„μž„).

 

Tomcat 3.2 이전 λ²„μ „μ—μ„œλŠ”, μœ μ €μ˜ μš”μ²­μ΄ λ“€μ–΄μ˜¬ λ•Œ λ§ˆλ‹€ Servlet을 μ‹€ν–‰ν•  Threadλ₯Ό ν•˜λ‚˜μ”© μƒμ„±ν–ˆκ³  μš”μ²­μ΄ λλ‚˜λ©΄ destoryν–ˆλ‹€κ³  ν•œλ‹€.

 

ν•˜μ§€λ§Œ λͺ¨λ“  μš”μ²­μ— λŒ€ν•΄ μŠ€λ ˆλ“œλ₯Ό μƒμ„±ν•˜κ³  μ†Œλ©Έν•˜λŠ” 것은 OS와 JVM에 λŒ€ν•΄ λ§Žμ€ 뢀담을 μ•ˆκ²¨μ€€λ‹€. λ™μ‹œμ— 일정 μ΄μƒμ˜ λ‹€μˆ˜ μš”μ²­μ΄ λ“€μ–΄μ˜¬ 경우 λ¦¬μ†ŒμŠ€(CPU와 λ©”λͺ¨λ¦¬ μžμ›) μ†Œλͺ¨μ— λŒ€ν•œ μ–΅μ œκ°€ μ–΄λ ΅λ‹€. 즉 λ™μ‹œλ‹€λ°œμ μΈ μš”μ²­μ„ μ²˜λ¦¬ν•˜μ§€ λͺ»ν•  κ°€λŠ₯성이 λ†’λ‹€.

 

이런 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄, 톰캣은 Thread Pool(μŠ€λ ˆλ“œ ν’€)을 ν™œμš©ν•œλ‹€.

 

μŠ€λ ˆλ“œν’€μ˜ κΈ°λ³Έ ν”Œλ‘œμš°λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • 첫 μž‘μ—…μ΄ λ“€μ–΄μ˜€λ©΄, core size만큼의 μŠ€λ ˆλ“œλ₯Ό 생성
  • μœ μ € μš”μ²­(Connection, Server socketμ—μ„œ acceptν•œ μ†ŒμΊ£ 객체)이 λ“€μ–΄μ˜¬ λ•Œλ§ˆλ‹€ μž‘μ—… 큐(queue)에 λ‹΄μ•„λ‘ 
  • core size의 μŠ€λ ˆλ“œ 쀑, μœ νœ΄μƒνƒœ(idle)인 μŠ€λ ˆλ“œκ°€ μžˆλ‹€λ©΄ μž‘μ—… νμ—μ„œ μž‘μ—…μ„ κΊΌλ‚΄ μŠ€λ ˆλ“œμ— μž‘μ—…μ„ ν• λ‹Ήν•˜μ—¬ μž‘μ—…μ„ 처리
    • λ§Œμ•½ μœ νœ΄μƒνƒœμΈ μŠ€λ ˆλ“œκ°€ μ—†λ‹€λ©΄, μž‘μ—…μ€ μž‘μ—… νμ—μ„œ λŒ€κΈ°
    • κ·Έ μƒνƒœκ°€ μ§€μ†λ˜μ–΄ μž‘μ—… 큐가 꽉 μ°¬λ‹€λ©΄, μŠ€λ ˆλ“œλ₯Ό μƒˆλ‘œ 생성
    • 3λ²ˆκ³Όμ •μ„ λ°˜λ³΅ν•˜λ‹€ μŠ€λ ˆλ“œ μ΅œλŒ€ μ‚¬μ΄μ¦ˆ 에 λ„λ‹¬ν•˜κ³  μž‘μ—…νλ„ 꽉 차게 되면, μΆ”κ°€ μš”μ²­μ— λŒ€ν•΄μ„  connection-refused 였λ₯˜λ₯Ό λ°˜ν™˜
  • νƒœμŠ€ν¬κ°€ μ™„λ£Œλ˜λ©΄ μŠ€λ ˆλ“œλŠ” λ‹€μ‹œ μœ νœ΄μƒνƒœλ‘œ λŒμ•„κ°.
    • 4-1. μž‘μ—…νκ°€ λΉ„μ–΄μžˆκ³  core sizeμ΄μƒμ˜ μŠ€λ ˆλ“œκ°€ μƒμ„±λ˜μ–΄μžˆλ‹€λ©΄ μŠ€λ ˆλ“œλ₯Ό destory

 

 

μŠ€λ ˆλ“œν’€ 생성 (ThreadPoolExecutor)

μœ„μ— μ„€λͺ…ν•œ μŠ€λ ˆλ“œν’€μ„ μžλ°”μ—μ„œ κ΅¬ν˜„ν•œ κ΅¬ν˜„μ²΄κ°€ ThreadPoolExecutor이닀. 

 

μ•„κΉŒ μœ„ν—€μ„œ μž‘μ„±ν–ˆλ˜ application.yml 쀑 일뢀이닀. 

server:
  tomcat:
    threads:
      max: 200 # 생성할 수 μžˆλŠ” thread의 총 개수
      min-spare: 10 # 항상 ν™œμ„±ν™” λ˜μ–΄μžˆλŠ”(idle) thread의 개수
    accept-count: 100 # μž‘μ—… 큐의 μ‚¬μ΄μ¦ˆ
    max-connections: 8192 # μ—°κ²° κ°€λŠ₯ν•œ connection의 수

 

threads의 두가지 섀정은 μŠ€λ ˆλ“œ μ΅œλŒ€ μ‚¬μ΄μ¦ˆ 및 core sizeλ₯Ό λ³€κ²½ν•  수 μžˆλ‹€. 

Spring Boot의 λ””ν΄νŠΈ 값은 각각 200개, 10개 이닀. 

 

 

accept-countλŠ” μž‘μ—…νμ˜ μ‚¬μ΄μ¦ˆμ΄λ‹€. Tomcat 에선 아무 μ˜΅μ…˜μ„ μ•ˆμ£Όλ©΄ Integer.MAX, 즉 21μ–΅xxx 둜 μ„€μ •λœλ‹€. μ΄λŠ” λ¬΄ν•œ λŒ€κΈ°μ—΄ μ „λž΅ 으둜, 아무리 μš”μ²­μ΄ 많이 듀어와도 core sizeλ₯Ό λŠ˜λ¦¬μ§€ μ•ŠλŠ”λ‹€λŠ” 정책이닀. 

 

max-connectionsλŠ” λ™μ‹œμ— μ„œλ²„κ°€ μš”μ²­μ„ μ²˜λ¦¬ν•  수 μžˆλŠ” Connection 수λ₯Ό μ˜λ―Έν•œλ‹€. 예λ₯Ό λ“€μ–΄ Max-Connection이 10 μΌλ•Œ 10개의 μš”μ²­μ΄ μ²˜λ¦¬μ€‘μ΄λΌλ©΄, block되고 μž‘μ—…νμ— λ“€μ–΄κ°€κ²Œλœλ‹€. 

 

 

 

 

 

πŸ€” threads.max 와 max-connection의 μ°¨μ΄λŠ” 그럼 뭘까??

λͺ‡ 가지 ν…ŒμŠ€νŠΈλ₯Ό 톡해 μ•Œμ•„λ³΄μ•˜λ‹€.

 

μ•„λž˜λŠ” μš”μ²­μ„ 보내기 μœ„ν•œ curl λͺ…λ Ήμ–΄λ“€μ˜ sh νŒŒμΌμ΄λ‹€. 

#!/bin/bash

curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &
curl --max-time 15 http://localhost:8080/test &

 

 

Test #1  [max-connections == threads.max]

μ΅œλŒ€ 컀λ„₯μ…˜ 수: 10개

μ΅œλŒ€ μŠ€λ ˆλ“œ 수: 10개

둜 μŠ€λ ˆλ“œλ₯Ό μ„€μ •ν•˜κ³  10번의 μš”μ²­μ„ λ³΄λ‚΄λ³΄μ•˜λ‹€.

server:
  tomcat:
    threads:
      max: 10 # 생성할 수 μžˆλŠ” thread의 총 개수
      min-spare: 1 # 항상 ν™œμ„±ν™” λ˜μ–΄μžˆλŠ”(idle) thread의 개수
    accept-count: 30 # μž‘μ—… 큐의 μ‚¬μ΄μ¦ˆ
    max-connections: 10 # μ—°κ²° κ°€λŠ₯ν•œ connection의 수

 

 

κ²°κ³ΌλŠ” 1μ΄ˆλ§Œμ— 10개의 μš”μ²­μ„ λ‹€ μ²˜λ¦¬ν–ˆλ‹€. 

 

 

 

 

Test #2  [max connections < thread.max]

μ΅œλŒ€ 컀λ„₯μ…˜ 수: 10개

μ΅œλŒ€ μŠ€λ ˆλ“œ 수: 5개

둜 μŠ€λ ˆλ“œλ₯Ό μ„€μ •ν•˜κ³  10번의 μš”μ²­μ„ λ³΄λ‚΄λ³΄μ•˜λ‹€.

server:
  tomcat:
    threads:
      max: 10 # 생성할 수 μžˆλŠ” thread의 총 개수
      min-spare: 1 # 항상 ν™œμ„±ν™” λ˜μ–΄μžˆλŠ”(idle) thread의 개수
    accept-count: 30 # μž‘μ—… 큐의 μ‚¬μ΄μ¦ˆ
    max-connections: 5 # μ—°κ²° κ°€λŠ₯ν•œ connection의 수

 

 

μ΅œλŒ€ μŠ€λ ˆλ“œ μˆ˜λŠ” 10κ°œμ§€λ§Œ 1 ~ 5 κΉŒμ§€ 총 5개의 μŠ€λ ˆλ“œλ§Œ μƒμ„±λ˜κ³  μ²˜λ¦¬ν•©λ‹ˆλ‹€.

λ”°λΌμ„œ 2μ΄ˆμ— 걸처 μš”μ²­μ„ μ²˜λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.

 

 


Test #3  [max connections > thread.max]

μ΅œλŒ€ 컀λ„₯μ…˜ 수: 5개

μ΅œλŒ€ μŠ€λ ˆλ“œ 수: 2개

둜 μŠ€λ ˆλ“œλ₯Ό μ„€μ •ν•˜κ³  10번의 μš”μ²­μ„ λ³΄λ‚΄λ³΄μ•˜λ‹€.

server:
  tomcat:
    threads:
      max: 2 # 생성할 수 μžˆλŠ” thread의 총 개수
#      min-spare: 1 # 항상 ν™œμ„±ν™” λ˜μ–΄μžˆλŠ”(idle) thread의 개수
    accept-count: 30 # μž‘μ—… 큐의 μ‚¬μ΄μ¦ˆ
    max-connections: 5 # μ—°κ²° κ°€λŠ₯ν•œ connection의 수

 

 

κ²°κ³ΌλŠ” max-connection은 5μ§€λ§Œ 2개의 μŠ€λ ˆλ“œλ§Œ 이용 κ°€λŠ₯ν•˜λ―€λ‘œ μŠ€λ ˆλ“œ1~2 총 2개의 μŠ€λ ˆλ“œλ‘œ 5μ΄ˆμ— 걸쳐 10개의 μš”μ²­μ„ μ²˜λ¦¬ν–ˆλ‹€.

 

 

 

 

 

 

 

 

4. κ²°λ‘ 

μ˜ˆμ „μ— μŠ€ν”„λ§μ„ 처음 배우기 μ‹œμž‘ν•  λ•Œ κ°•μ˜μ—μ„œ "μŠ€ν”„λ§μ€ μ„œλ²„μ— μŠ€λ ˆλ“œ 풀이 있고 μŠ€λ ˆλ“œλ₯Ό 꺼내와 μš”μ²­μ„ μ²˜λ¦¬ν•˜κ³  λ°˜λ‚©ν•œλ‹€" 라고 배운 기얡이 λ‚œλ‹€.

 

그땐 μŠ€λ ˆλ“œμ˜ κ°œλ…λ„ 잘 λͺ¨λ₯Έμ±„λ‘œ λ“€μ—ˆλŠ”λ°, λͺ‡ 년이 μ§€λ‚˜κ³  κΆκΈˆν•΄μ Έ 찾아보고 μ •λ¦¬ν•˜κ²Œ λ˜μ—ˆλ‹€. 

이젠 톰캣이 μ–΄λ–»κ²Œ μŠ€λ ˆλ“œλ₯Ό κ΄€λ¦¬ν•˜κ³ , μ–΄λ–»κ²Œ 값을 λ°”κΏ€ 수 μžˆλŠ”μ§€ μ–΄λŠμ •λ„ μ•Œκ²Œ λ˜μ—ˆμ§€λ§Œ 쑰금 더 ν•™μŠ΅ν•œ 후에 각자의 μ„œλ²„μ— 맞게 νŠœλ‹ν•˜κ³  μ„±λŠ₯ κ°œμ„ κΉŒμ§€ 해보기엔 μ‹Άλ‹€. 

 

ν˜„μž¬ μ°½μ—… ν”„λ‘œμ νŠΈμ—μ„œ 개발 쀑인 μ„œλΉ„μŠ€κ°€ μžˆλŠ”λ°, κ΅¬ν˜„μ΄ μ™„λ£Œλ˜κ³  배포λ₯Ό μ•žλ‘”λ‹€λ©΄ λΆ€ν•˜ ν…ŒμŠ€νŠΈμ™€ λͺ¨λ‹ˆν„°λ§μ„ 톡해 ν†°μΊ£ νŠœλ‹μ„ 진행해보고 기둝할 생각이닀. 

 

 

 

배움엔 끝이 μ—†λ‹€..!