0. νκ²½ μ€μ
Threads μ 3000μ ramp-up μ 1μ 1λΆ μ§μμΌλ‘ ν μ€νΈλ₯Ό μ€μ νκ³ ν¬μ€ μ²΄ν¬ APIμ μ μ©νλ λ°κ³Ό κ°μ μλ¬λ₯Ό λ§λ¬μ΅λλ€.
java.net.SocketException: Connection reset
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:328)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:355)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:808)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)
νμ¬ νλ‘μ νΈμμ μ°κ³ μλ ν°μΊ£μ μ°λ λ μλ₯Ό 3000κ°λ‘ λλ €λμ μνμ΄κ³ , λ°λ‘ DBλ₯Ό κ±°μΉμ§ μλ ν¬μ€ 체ν¬
API μκΈ° λλ¬Έμ, μ΄λ‘ μ λͺ¨λ μμ²μ΄ νλμ μ°λ λλ₯Ό μ μ νλ©° SPRING MVC κΉμ§ μμ μλ΅μ λ°νν΄μΌ νμ΅λλ€. νμ§λ§ μλ¬μ¨ 46.14%
λ‘ μ λ°μ΄ λλ μ°λ λκ° μμ²μ μ€ν¨ νμμ΅λλ€. μ μ μΈνλΌνμ΄ μ΄κ²μ λμνκΈ° μν΄ μκ°νκ³ , μ€ν νλ λ°©μμ λ€μκ³Ό κ°μ΅λλ€.
1. WAS λκΈ° ν (accept-count) λ리기
μμ²μ΄ νλ²μ λ€μ΄μ, WAS μ°λ λ μ μ μ λν κ²½μ μνκ° μ겨μ μ°λ λ μ μ μ μ°¨μ§μ΄ μκΈ°λ κ²μ΄ μμΈμ΄λΌ νλ¨νμκ³ , yaml νμΌμμ λ€μκ³Ό κ°μ΄ WASμ μμ²λ€μ΄ λκΈ° νλ accept-count
λΌλ λκΈ°νμ κ°μ λλ €μ£Όμμ΅λλ€.
server:
port: 8080
tomcat:
threads:
max: 3000 # μ΅λ μ€λ λ κ°μ (κΈ°λ³Έκ°: 200)
min-spare: 500 # μ΅μ μ μ§ μ€λ λ κ°μ (κΈ°λ³Έκ°: 10)
accept-count: 10000 # μμ² λκΈ° ν ν¬κΈ° (κΈ°λ³Έκ°: 100)
connection-timeout: 30s # ν΄λΌμ΄μΈνΈκ° μλ΅μ κΈ°λ€λ¦¬λ μ΅λ μκ°
max-connections: 10000 # λμμ μ°κ²° κ°λ₯ν μ΅λ 컀λ₯μ
μ (κΈ°λ³Έκ°: 8192)
νμ§λ§ μ΄λ¬ν μ€μ λ³κ²½μλ λΆκ°νκ³ , μ¬μ ν μ€νμ΄ν¬ μ± μμ²μ΄ μμ κ²½μ°, μλ²μμ λͺ¨λ μμ²μ λ°μ§ λͺ»ν μ±, μμ κ°μ SocketConnection Error
κ° λ°μνμμ΅λλ€.
2. μ±λ₯ ν μ€νΈ ν΄ λΉκ΅ λ° ν΄λΌμ΄μΈνΈμ λ¬Έμ μν© νμΈ
μ μ μΈνλΌνμ λ μ΄μ λ¬Έμ κ° μμ© κ³μΈ΅μΈ WASμμ μλ€κ³ νλ¨νμ§ μμμ΅λλ€. μ΄μ λ λ€μκ³Ό κ°μ΅λλ€.
SPRING-actuator
μμ‘΄μ±μ μΆκ°νκ³ ,actuator
μμ μ 곡νλ λ€μμ APIλ₯Ό ν΅ν΄,currentThreadsBusy(νμ¬ μ²λ¦¬ μ€μΈ μμ² μ)
,currentThreadCount(μ 체 μμ±λ μ€λ λ μ)
λκ°μ§ μ¬νμ νμΈνμ΅λλ€.curl http://localhost:8080/actuator/metrics/jvm.threads.states (runnable μνμ μ°λ λ νμΈ) curl http://localhost:8080/actuator/metrics/jvm.threads.live (νμ±νλ μ°λ λ νμΈ)
- μΌλ°
netstat
,eBPF
ν΄μ νμ©ν΄, μ΄μ체μ 컀λμ accpet νμ ν¬κΈ° νμΈ (yamlλ‘ λλ Έλ accept-count) sudo bpftrace -e 'kprobe/tcp_v4_syn_recv_sock { @[comm] = count(); }'
netstat -anp | grep :8080 | grep SYN_RECV | wc -l
μ΄λ¬ν μν©μ κ±°μ³€μ§λ§, μ 체 ν μ¬μ΄μ¦μ Thread μλ κ°κ° λ§ κ°μ 3000κ°λ‘ μ νν νμ±λμμ§λ§, 1000κ° μ΄μμ΄ ν λ²μ λ€μ΄μ€λ μ€νμ΄ν¬ μ± μμ²μ΄ μμ κ²½μ°, λκΈ°νλ μ΅λ 128κ° λ°μ νμ©νμ§ λͺ»νμκ³ , μ°λ λ λν 200κ° μ΄μμ΄ λμ νμ±νκ° λμ§ μμμ΅λλ€.
κ·Έλμ μ μ μΈνλΌνμ μ±λ₯ ν μ€νΈ ν΄λ‘ μ¬μ©νλ Jmeterκ° μ€μ λ‘ μμ²μ μ λλ‘ λ³΄λ΄μ§ λͺ»νλ μν©μ΄λΌκ³ μκ°νμ΅λλ€. μ€μ λ‘ μ λ‘컬 μ»΄ν¨ν°μμμ Jmeterλ λμΌ μμ²μμ μ€λ₯μ¨μ΄ μ΅λ 6.46% μμ§λ§, μΈνλΌ κ²½νλμ λ‘컬 μ»΄ν¨ν°μμ μ μ‘ νμ μ μ€λ₯μ¨μ΄ 92.49%κ° μ°ν μμκΈ° λλ¬Έμ λλ€. μ΄ν Jmeterμ λν νλμ μ§ννλ € νλ€κ°, μ μ‘ κ³μΈ΅μμμ μ€λ₯μΌ μ μκ² λ€λ μκ°μ΄ λ€μ΄, 리λ μ€μ ν¨ν· μ²λ¦¬μ λν νμ΅ ν μ΄λ₯Ό μ μ©νμμ΅λλ€.
3. μ΄μ체μ μ μ μ‘ κ³μΈ΅μμμ ν¨ν· μ²λ¦¬μ λνμ¬ (Back-log overflow)
λ¨Όμ TCP 체κ³μμ μλ²μ ν΄λΌμ΄μΈνΈκ° 첫 μ°κ²°μ νλ 3-way handshaking κ³Όμ
μ λν΄ κ°λ¨ν μ€λͺ
μ νκ³ κ°κ² μ΅λλ€.
ν΄λΉ κ³Όμ μ μλ²μ ν΄λΌμ΄μΈνΈκ° λ³Έ μμ²μ μ£Όκ³ λ°κΈ° μ μ μλ‘μ μ°κ²°μ μλ‘κ° νμ νκΈ° μν΄ κ±°μΉλ
3-way handshaking κ³Όμ μ
λλ€. μ¬κΈ°μ μλ²κ° ν΄λΌμ΄μΈνΈμκ² SYN + ACK
μμ²μ λ³΄λΈ ν, ν΄λΌμ΄μΈνΈλ‘λΆν° λ§μ§λ§ ACK
μμ²μ κΈ°λ€λ¦΄ λμ μ¦ TCB (λ€νΈμν¬ μ€νμ μ μ₯λ TCP λΈλ‘)
κ° half-open
μνμΌ λ, μ΄ TCBλ SYN ν
μ μ μ₯λ©λλ€. μ΄ν ACK μμ²μ΄ ν΄λΌμ΄μΈνΈλ‘λΆν° μ¨ κ²½μ° (μ¦ TCBμ μνκ° ESTABLISHED
μΌ λ, ) ν΄λΉ TCBλ₯Ό SYN νμμ κΊΌλ΄ ACCEPT ν
λ‘ λ³΄λ
λλ€. μ΄ λνμ ν¬κΈ°λ κΈ°λ³Έμ μΌλ‘ 128λ‘ μ ν¬κ° 2000κ° 3000κ°μ μμ²μ 보λ΄λ, κ·Έκ² μ€ 128κ°λ§ μ ν¨νκ² WASμκ² μ λ¬λλ κ²μ
λλ€.
κ·Έλ¬λ©΄ μ°λ¦¬κ° μ€μ ν yamlμ accept-countλ μ μ μ©μ΄ λμ§ μλκ°?
ν΄λΉ accept-count
λ WAS λ΄μ listen BackLogμ ν¬κΈ°λ₯Ό μ€μ νλ κ°μ
λλ€. μ¦ Backlogμ νλ‘λΆν° ν λ²μ κ°μ Έμ€λ TCB λ°μ΄ν°μ ν¬κΈ° μ
λλ€.
μ¬κΈ°μ Javaμ Server Sockertμ΄ λ¦¬λ
μ€ μ»€λμ λ΄λΆ C μ½λ ν¨μ μ€ listen(fd, backlog)
λ₯Ό λΆλ₯΄κ² λκ³ , ν΄λΉ listen(fd, backlog)μ λ΄λΆλ μ΄λ κ² λμ΄ μμ΅λλ€. μμ listen(fd, backlog)
μμ backlog
λ javaμμ μ€μ ν "λ μ΄μ λκΉμ§ ν λ²μ λ°μ μ μμ΄"λΌλ κ°μ accept-count
μ
λλ€.
int __sys_listen(int fd, int backlog)
{
...
if (backlog > somaxconn)
backlog = somaxconn;
...
}
somaxconn
μ 컀λ λ΄λΆμ μ€μ νλ μ€μ νμ ν¬κΈ° μ
λλ€. μ¦ backlogλ₯Ό μ무리 ν¬κ² μ€μ ν΄λ, somaxconnμ κ°μ΄ κ·Έλ³΄λ€ μμΌλ©΄ μ무 μμ©μ΄ μλ κ²μ
λλ€. λ°λΌμ μ€νμ΄ν¬μ± μμ²μ λν μλ²½ν λμμ±
μ μν΄μλ, WAS νλκ³Ό λλΆμ΄ OSμ 컀λ νλλ νμ μ
λλ€.
λ°λΌμ νμ¬ WAS μλ²κ° μ¬λΌκ° μ΄μ체μ μ λ€μ΄κ° TCP Back-logμ ν¬κΈ°λ₯Ό νμΈνμ μΌ ν©λλ€. μ λ 리λ μ€ μλ²μμ κ°μ νκ³ λ€μ κ³Όμ μ μ§ννκ² μ΅λλ€.
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.core.netdev_max_backlog
μλ§ κΈ°λ³Έ μ€μ μ somaxconnμ κ°μ΄ 128λ‘ λμ΄ μμ ν λ° μ΄λ₯Ό μμ μ μ΄μ체μ μ λ©λͺ¨λ¦¬ μν©, μ€νμ λ§κ² μ‘°μ νμκΈ° λ°λλλ€.
# μ°κ²° μμ² ν ν¬κΈ° μ¦κ°
sudo sysctl -w net.core.somaxconn=65535
# SYN λκΈ° ν μ¦κ° (3-way handshake λμ 보λ₯λ μμ² ν)
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=65535
# NIC μμ λκΈ° ν μ¦κ°
sudo sysctl -w net.core.netdev_max_backlog=65535
4. μκ²°
μ΄λ¬ν κ³Όμ μ κ±°μ³, μ ν¬ μλ²λ 900RPSμ μ€νμ΄ν¬ μ± ν μ€νΈμ 1800RPS μ΄μμ λΆν ν μ€νΈλ₯Ό 견λ μ μλ μλ²λ‘ μ¬νμ νμμ΅λλ€. μ΄νμλ μ ν¬ μλ² μ€ μ κ° λ§λ API 25κ°μ νμλ€μ APIμ λν΄ ν΅ν© ν μ€νΈλ₯Ό μ§νν΄ λ³΄κ² μ΅λλ€.