Connection vs Read Timeout

β€œAPI 응닡 μ—†μŒβ€.

νŠΉμ • API 호좜이 κΈ΄ μ‹œκ°„ λ™μ•ˆ hanging 쀑일 수 μžˆλ‹€.

이런 μ‹μœΌλ‘œ κ°œλ°œν•˜λ©΄μ„œ ν•œ λ²ˆμ―€μ€ λ¬΄ν•œμ • λ‘œλ”© μŠ€ν”Όλ„ˆλ₯Ό κ²½ν—˜ν•œ 적이 μžˆμ„ 수 μžˆλ‹€.

이 문제의 μƒλ‹Ήμˆ˜λŠ” νƒ€μž„μ•„μ›ƒκ³Ό 관련이 μžˆμ„ κ°€λŠ₯성이 크닀. κ·Έλƒ₯ κΈ°λ³Έκ°’μœΌλ‘œ λ‘κ±°λ‚˜, μ–΄λ¦Όμ§μž‘μœΌλ‘œ β€œλŒ€μΆ© 5초?” ν•˜κ³  μ„€μ •ν•˜κΈ°μ—λŠ” λ„ˆλ¬΄ μ€‘μš”ν•œ 값이닀.

Connection Timeout vs Read Timeout: 차이점

Connection Timeout: TCP 3-way handshake μ‹œκ°„

Connection νƒ€μž„μ•„μ›ƒμ„ μ‰½κ²Œ 생각해보면 β€œμƒλŒ€λ°©μ—κ²Œ μ „ν™”λ₯Ό κ±°λŠ” μ‹œκ°„β€ 이닀.

  • μƒλŒ€λ°©μ—κ²Œ μ „ν™”λ₯Ό κ±Έκ³ , νŠΉμ • μ‹œκ°„μ•ˆμ— μ—°κ²°λ˜μ§€ μ•ŠμœΌλ©΄ λŠκΈ΄λ‹€.

  • ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„ κ°„μ˜ TCP 3-way handshakeλ₯Ό 톡해 논리적인 연결을 μˆ˜λ¦½ν•˜λŠ” λ°κΉŒμ§€ ν—ˆμš©λ˜λŠ” μ‹œκ°„μ΄λΌκ³ λ„ ν•  수 μžˆλ‹€. (연결이 μ„±λ¦½λ˜κΈ°κΉŒμ§€μ˜ μ΅œλŒ€ λŒ€κΈ° μ‹œκ°„)

즉, Connection Timeout은 TCP μ—°κ²° μžμ²΄κ°€ μ„±λ¦½λ˜μ§€ μ•Šμ€ μƒνƒœμ—μ„œ μ—°κ²° μ‹œλ„κ°€ 일정 μ‹œκ°„ 내에 μ™„λ£Œλ˜μ§€ λͺ»ν–ˆμ„ λ•Œ λ°œμƒν•œλ‹€.

μœ„ κ³Όμ •μ—μ„œ μ†Œμš”λ˜λŠ” μ‹œκ°„μ΄ Connection Timeout이닀.

Connection Timeout이 λ°œμƒν•˜λŠ” 쑰건

  • μ„œλ²„κ°€ μ£½μ—ˆμ„ λ•Œ

  • λ°©ν™”λ²½μ—μ„œ νŒ¨ν‚·μ„ 막을 λ•Œ

  • λ„€νŠΈμ›Œν¬ μž₯λΉ„ 이슈 λ“± (λ„€νŠΈμ›Œν¬ κ²½λ‘œμ— 문제 λ°œμƒ)

  • μŠ€λ ˆλ“œ ν’€ 고갈과 같은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 병λͺ©λ„, accept 호좜이 μ§€μ—°λ˜λ©΄μ„œ κ°„μ ‘μ μœΌλ‘œ Connection Timeout의 원인이 될 수 μžˆλ‹€.

Read Timeout: μ—°κ²° 이후, 닡변을 κΈ°λ‹€λ¦¬λŠ” μ‹œκ°„

Read νƒ€μž„μ•„μ›ƒμ€ β€œμ „ν™”μ— μ—°κ²°λ˜κ³ , μƒλŒ€λ°©μ΄ λ‹΅λ³€ν•˜κΈ°κΉŒμ§€ κΈ°λ‹€λ¦¬λŠ” μ‹œκ°„β€μ΄λ‹€.

이미 TCP 연결이 μ„±λ¦½λœ μƒνƒœμ—μ„œ μš”μ²­μ„ 보낸 μͺ½μ΄ 응닡 데이터λ₯Ό 일정 μ‹œκ°„ λ™μ•ˆ λ°›μ§€ λͺ»ν–ˆμ„ λ•Œ λ°œμƒν•œλ‹€.

  • μ „ν™”κ°€ μ—°κ²°λ˜κ³ , μƒλŒ€λ°©μ΄ 아무 λŒ€λ‹΅ 없이 μΉ¨λ¬΅ν•˜λŠ” 상황을 μ–Όλ§ˆλ‚˜ 기닀릴지 μ •ν•˜λŠ” μ‹œκ°„μ΄λ‹€.

  • 연결은 μ„±κ³΅ν–ˆμ§€λ§Œ, μ„œλ²„κ°€ μš”μ²­μ„ μ²˜λ¦¬ν•˜κ³  데이터λ₯Ό 보내주기 μ „κΉŒμ§€ λ¬΄ν•œμ • 기닀리지 μ•Šλ„λ‘ λ°©μ§€ν•˜λŠ” μ•ˆμ •μž₯μΉ˜μ΄κΈ°λ„ ν•˜λ‹€.

연결은 μ„±κ³΅ν–ˆμ§€λ§Œ, μ‹€μ œ 데이터λ₯Ό μ£Όκ³ λ°›λŠ” κ³Όμ •μ—μ„œμ˜ μ΅œλŒ€ λŒ€κΈ° μ‹œκ°„μ΄λ‹€.

Read Timeout이 λ°œμƒν•˜λŠ” 쑰건

  • μ„œλ²„μ—μ„œ μš”μ²­ μ²˜λ¦¬κ°€ 였래 κ±Έλ¦¬λŠ” 경우

  • 응닡 데이터가 큰 경우

  • 쀑간 λ„€νŠΈμ›Œν¬μ—μ„œ νŒ¨ν‚· μœ μ‹€μ΄ λ°œμƒν•œ 경우 (ν˜Ήμ€ λ„€νŠΈμ›Œν¬ λΆˆμ•ˆμ •)

Timeout을 μœ„ν•œ Golden Principle

κ·Έλž˜μ„œ 각 timeout 값을 μ„€μ •ν•˜κΈ° μœ„ν•΄μ„œ μ„œλΉ„μŠ€λ§Œμ˜ κ·œμΉ™μ΄ μ‘΄μž¬ν•΄μ•Ό ν•œλ‹€.

μ•„λž˜ 두 쑰건을 κ³ λ €ν•  수 μžˆλ‹€.

  1. λ„€νŠΈμ›Œν¬λŠ” μ™„λ²½ν•˜μ§€ μ•Šλ‹€. νŒ¨ν‚· μœ μ‹€μ€ μž₯μ•  상황이 μ•„λ‹ˆμ–΄λ„ μ–Έμ œλ“  일어날 수 μžˆλ‹€.

  • κ°„ν˜ˆμ μΈ νŒ¨ν‚· μœ μ‹€μ€ 정상적인 상황이닀. νƒ€μž„μ•„μ›ƒμ΄ λ„ˆλ¬΄ 짧으면 정상적인 λ„€νŠΈμ›Œν¬ 지연도 μ—λŸ¬λ‘œ μ²˜λ¦¬ν•˜κ²Œ λœλ‹€.

  1. 정말 λ„€νŠΈμ›Œν¬ μž₯μ• κ°€ λ°œμƒν–ˆλ‹€λ©΄, μ΅œλŒ€ν•œ 빨리 μΈμ§€ν•˜κ³  λŒ€μ‘ν•΄μ•Ό ν•œλ‹€.

  • λ„€νŠΈμ›Œν¬μ— μ§„μ§œ λ¬Έμ œκ°€ λ°œμƒν•œ 것이라면 μ΅œλŒ€ν•œ 빨리 감지해야 ν•œλ‹€. νƒ€μž„μ•„μ›ƒμ΄ λ„ˆλ¬΄ κΈΈλ©΄ μž₯μ•  λŒ€μ‘μ΄ λŠ¦μ–΄μ§„λ‹€.

νƒ€μž„μ•„μ›ƒμ„ λ„ˆλ¬΄ 짧게 작으면, 가끔 λ°œμƒν•˜λŠ” νŒ¨ν‚· μœ μ‹€μ—λ„ μ„œλΉ„μŠ€κ°€ μ‹€νŒ¨ν–ˆλ‹€κ³  νŒλ‹¨ν•˜κ²Œ 되고, λ°˜λŒ€λ‘œ λ„ˆλ¬΄ 길게 작으면, μ‹€μ œ μž₯μ• κ°€ λ°œμƒν–ˆμ„ λ•Œ μ‚¬μš©μžλŠ” ν•˜μ—Όμ—†μ΄ 기닀리고 μ‹œμŠ€ν…œμ€ 문제λ₯Ό λ„ˆλ¬΄ 늦게 발견게 λœλ‹€.

λ”°λΌμ„œ μ΄λŸ¬ν•œ 쑰건을 μ„€μ •ν•˜κ³  이λ₯Ό λ§Œμ‘±ν•˜λŠ” 기쀀을 μ •ν•˜λŠ” 게 μ’‹λ‹€.

νŒ¨ν‚· μœ μ‹€κ³Ό μž¬μ „μ†‘ λ©”μ»€λ‹ˆμ¦˜

LinuxλŠ” SYN νŒ¨ν‚·μ΄ μœ μ‹€λ˜λ©΄ 1초 ν›„ μž¬μ „μ†‘ν•œλ‹€.

즉, Linux Kernel에 ν•˜λ“œμ½”λ”©λœ 초기 μž¬μ „μ†‘ νƒ€μž„μ•„μ›ƒ, InitRTO 값을 μ‚¬μš©ν•œλ‹€λŠ” 것이닀.

  • 이 값은 기본적으둜 1초이며, RFC 6298에 λ”°λ₯Έ 초기 RTO 값이라고 ν•œλ‹€.

  • InitRTOλŠ” TCP Handshake 쀑 첫 SYN νŒ¨ν‚·μ—λ§Œ 적용되며, μ΄ν›„μ˜ RTOλŠ” RTT 츑정에 κΈ°λ°˜ν•΄ λ™μ μœΌλ‘œ μ‘°μ •λœλ‹€.

  • SYN νŒ¨ν‚·μ΄ ν•œ 번 μœ μ‹€λœλ‹€λ©΄ μ΅œμ†Œν•œ 연결을 λ§ΊλŠ”λ° 1초 μ΄μƒμ˜ μ‹œκ°„μ΄ μ†Œμš”λœλ‹€.

TCP RTOλŠ” μ—°κ²° 이후에도 계속 μ‚¬μš©λ˜λŠ” κ°œλ…μ΄λ‹€.

  1. μ—°κ²° μ‹œ (SYN 단계)

  • RTTλ₯Ό λͺ¨λ₯΄λ‹ˆκΉŒ InitRTO(1초) μ‚¬μš©

  • SYN νŒ¨ν‚· μœ μ‹€ μ‹œ 1초 ν›„ μž¬μ „μ†‘

  1. μ—°κ²° ν›„ (데이터 톡신)

  • SYN + ACKλ₯Ό λ°›μœΌλ©΄μ„œ 첫 RTT μΈ‘μ •

  • 이후 RTT 기반으둜 RTO 동적 계산

  • μ΅œμ†Œκ°’: 200ms (tcp_rto_min)

  • μ΅œλŒ€κ°’: 120초 (tc_rto_max)

즉, 초기 μ—°κ²° μ‹œ RTO 값이 μ •ν•΄μ§€κ³ , 이후 νŒ¨ν‚· κ΅ν™˜ κ³Όμ •μ—μ„œ μΈ‘μ •λ˜λŠ” RTT(왕볡 μ‹œκ°„)λ₯Ό λ°˜μ˜ν•΄ RTOκ°€ 계속 λ³€ν™”ν•˜κ³ , μ—°κ²° 이후에도 RTOλŠ” λ³€ν™”ν•˜λ©°, λ„€νŠΈμ›Œν¬ 상황에 맞좰 λ™μ μœΌλ‘œ μ‘°μ •λ˜λŠ” 값이닀.

μ‹€μ œ μ‹œκ°„ 계산:

  • 1번 μœ μ‹€: 1초 μ§€μ—°

  • 2번 μœ μ‹€: 1 + 2 = 3초 μ§€μ—°

  • 3번 μœ μ‹€: 1 + 2 + 4 = 7초 μ§€μ—°

Connection Timeout

컀λ„₯μ…˜ νƒ€μž„μ•„μ›ƒμ€ TCP μ—°κ²° κ³Όμ •μ—μ„œ λ°œμƒν•œλ‹€. μ—¬κΈ°μ„œ νŒ¨ν‚·μ΄ μœ μ‹€λ  수 μžˆλŠ” κ²½μš°λŠ” 크게 μ„Έ 가지이닀.

  1. ν΄λΌμ΄μ–ΈνŠΈκ°€ 보낸 μ—°κ²° μš”μ²­(SYN) νŒ¨ν‚· μœ μ‹€ (ν΄λΌμ΄μ–ΈνŠΈ β†’ [μœ μ‹€] β†’ μ„œλ²„)

  2. μ„œλ²„κ°€ 보낸 μ—°κ²° 수락(SYN + ACK) νŒ¨ν‚· μœ μ‹€ (ν΄λΌμ΄μ–ΈνŠΈ ← [μœ μ‹€] ← μ„œλ²„)

  3. ν΄λΌμ΄μ–ΈνŠΈκ°€ 보낸 μ΅œμ’… 확인(ACK) νŒ¨ν‚· μœ μ‹€ (ν΄λΌμ΄μ–ΈνŠΈ β†’ [μœ μ‹€] β†’ μ„œλ²„)

μ—¬κΈ°μ„œ μ€‘μš”ν•œ κ°œλ…μ΄ ν•˜λ‚˜ μžˆλŠ”λ°, RTO(Retransmission Timeout, μž¬μ „μ†‘ νƒ€μž„μ•„μ›ƒ)이닀.

νŒ¨ν‚·μ„ 보내고 이 μ‹œκ°„λ§ŒνΌ 응닡이 μ—†μœΌλ©΄ β€œμ•„, μœ μ‹€λκ΅¬λ‚˜β€ν•˜κ³  μž¬μ „μ†‘μ„ μ‹œλ„ν•œλ‹€.

특히, 연결을 처음 μ‹œλ„ν•  λ•ŒλŠ” RTT(Rount Trip Time, 데이터 왕볡 μ‹œκ°„)λ₯Ό λͺ¨λ₯΄κΈ° λ•Œλ¬Έμ—, InitRTO 값을 μ‚¬μš©ν•œλ‹€.

μœ„μ—μ„œ μ–ΈκΈ‰ν•œ νŒ¨ν‚· μœ μ‹€ μ‹œμ˜ μ‹œκ°„ 계산을 κΈ°μ€€μœΌλ‘œ,

  • Connection Timeout을 3초둜 μ„€μ •ν–ˆλ‹€λ©΄

  • ν•œ 번의 νŒ¨ν‚· μœ μ‹€μ€ ν—ˆμš©ν•˜κ³ , μ—°μ†λœ 두 번의 νŒ¨ν‚· μœ μ‹€μ΄ λ°œμƒν–ˆμ„ λ•Œ λΉ λ₯΄κ²Œ μ‹€νŒ¨ν•˜κ² λ‹€λŠ” μ˜λ―Έμ΄λ‹€.

Read Timeout: μ„œλΉ„μŠ€ νŠΉμ„±μ— 맞게 μ„€μ •

Read Timeout은 Connection Timeout보닀 λ³΅μž‘ν•˜λ‹€. κ³ λ €ν•΄μ•Ό ν•  μš”μ†Œκ°€ 더 많기 λ•Œλ¬Έμ΄λ‹€.

  • 연결이 되고 λ‚˜λ©΄ RTT 기반으둜 RTO(Retransmission Timeout)λ₯Ό κ³„μ‚°ν•œλ‹€.

  • μ„œλ²„μ˜ 처리 μ‹œκ°„(Server-Side Processing Time)을 ν•¨κ»˜ κ³ λ €ν•΄μ•Ό ν•œλ‹€.

  • μ„œλ²„ 처리 μ‹œκ°„: μ„œλ²„κ°€ ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ„ λ°›μ•„ μ²˜λ¦¬ν•˜λŠ” 데 κ±Έλ¦¬λŠ” μ‹€μ œ μ‹œκ°„

  • RTT: ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ„œλ²„λ‘œ μš”μ²­μ΄ μ „λ‹¬λ˜κ³ , λ‹€μ‹œ 응닡이 ν΄λΌμ΄μ–ΈνŠΈμ— λ„μ°©ν•˜λŠ” 데 κ±Έλ¦¬λŠ” 총 왕볡 μ‹œκ°„

  • νŒ¨ν‚· μž¬μ „μ†‘ μ‹œκ°„: λ„€νŠΈμ›Œν¬ μƒμ—μ„œ νŒ¨ν‚·μ΄ μœ μ‹€λ˜μ—ˆμ„ 경우 μž¬μ „μ†‘μ΄ μΌμ–΄λ‚˜λ©΄μ„œ μΆ”κ°€λ‘œ μ†Œμš”λ˜λŠ” μ‹œκ°„(RTO)λ₯Ό ν¬ν•¨ν•œλ‹€.

μ—¬κΈ°μ„œ RTTλ‚˜ νŒ¨ν‚· μž¬μ „μ†‘ μ‹œκ°„λ³΄λ‹€ μ„œλ²„ 처리 μ‹œκ°„μ΄ 전체 μ‹œκ°„μ„ μ’Œμš°ν•˜λŠ” κ²½μš°κ°€ λŒ€λΆ€λΆ„μ΄λ‹€.

ν˜ΈμΆœν•œ APIκ°€ λ³΅μž‘ν•œ DB 쿼리λ₯Ό μˆ˜ν–‰ν•˜κ±°λ‚˜ λ‹€λ₯Έ μ„œλΉ„μŠ€λ₯Ό ν˜ΈμΆœν•œλ‹€λ©΄, 수백 ms ~ μˆ˜μ΄ˆκ°€ 걸릴 수 μžˆλ‹€.

κ·Έλ ‡κΈ° λ•Œλ¬Έμ— Read Timeout을 μ„€μ •ν•˜λŠ” κ°€μž₯ 쒋은 방법은 데이터에 κΈ°λ°˜ν•˜λŠ” 것이닀.

λ°±μ—”λ“œ μ„œλΉ„μŠ€μ— APM이 κ΅¬μ„±λ˜μ–΄ μžˆλ‹€κ³  κ°€μ •ν•˜μž.

μš°λ¦¬λŠ” APM을 톡해 ν˜ΈμΆœν•˜λ €λŠ” API의 P95 λ˜λŠ” P99 응닡 μ‹œκ°„μ„ 확인할 수 μžˆλ‹€.

  • P99: 전체 μš”μ²­ 쀑 99%κ°€ 이 μ‹œκ°„μ•ˆμ— μ‘λ‹΅ν–ˆλ‹€λŠ” 의미둜, λŒ€λΆ€λΆ„μ˜ 정상적인 경우λ₯Ό ν¬ν•¨ν•˜λŠ” μ•ˆμ •μ μΈ μ§€ν‘œλ‹€.

κ·Έλž˜μ„œ A μ„œλΉ„μŠ€μ˜ P99 응닡 μ‹œκ°„μ΄ 700ms라면, Read Timeout은 λ„€νŠΈμ›Œν¬ 상황을 κ³ λ €ν•œ 버퍼(e.g., 300ms ~ 500ms)λ₯Ό 더해 1초 ~ 1.2초둜 μ„€μ •ν•˜λŠ” 것이 맀우 합리적일 수 μžˆλ‹€.

λ§Œμ•½ API 응닡 μ‹œκ°„μ„ μ „ν˜€ λͺ¨λ₯΄λŠ” 상황이라면, 1~3초의 보수적인 κ°’μœΌλ‘œ μ‹œμž‘ν•˜κ³  λ°˜λ“œμ‹œ λͺ¨λ‹ˆν„°λ§μ„ 톡해 μ‘°μ •ν•˜λŠ” 것이 쒋을 것 κ°™λ‹€.

μΆ”κ°€ λ‚΄μš©

  1. Read Timeout은 무쑰건 ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ„€μ •ν•œλ‹€.

Read Timeout은 μš”μ²­μ„ 보낸 μͺ½(ν΄λΌμ΄μ–ΈνŠΈ)이 응닡을 μ–Όλ§ˆλ‚˜ 기닀릴지λ₯Ό κ²°μ •ν•˜λŠ” 정책이기 λ•Œλ¬Έμ΄λ‹€. μ„œλ²„λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ μ–Όλ§ˆλ‚˜ 기닀렀쀄지 μ•Œμ§€ λͺ»ν•œλ‹€.

  1. Read Timeout μ„œλ²„ 둜그

μ§μ ‘μ μœΌλ‘œ "Read Timeout"μ΄λΌλŠ” λ‘œκ·ΈλŠ” 남지 μ•ŠλŠ”λ‹€.

μ„œλ²„λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ νƒ€μž„μ•„μ›ƒμœΌλ‘œ λŠμ–΄μ§„ 사싀을 λͺ¨λ₯Έλ‹€. μ„œλ²„λŠ” μžμ‹ μ˜ μž‘μ—…μ„ μ™„λ£Œν•˜κ³  ν΄λΌμ΄μ€ν…Œμ—κ²Œ 응닡을 λ³΄λ‚΄λŠ” μˆœκ°„, ν΄λΌμ΄μ–ΈνŠΈκ°€ 컀λ„₯μ…˜μ„ λŠμ—ˆλ‹€λŠ” 사싀을 μΈμ§€ν•˜κ³  μ„œλ²„μ— λ‹€μŒκ³Ό 같은 λ„€νŠΈμ›Œν¬ 였λ₯˜λŠ” 남길 수 μžˆλ‹€.

  • java.net.SocketException: Broken pipe

  • Connection reset by peer

μ€‘μš”ν•œ 것은, μ„œλ²„μ˜ μž‘μ—… μžμ²΄λŠ” μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ˜μ—ˆμ„ 수 있기 λ•Œλ¬Έμ— ν΄λΌμ΄μ–ΈνŠΈλŠ” "μ‹€νŒ¨"둜 μ²˜λ¦¬ν–ˆμ§€λ§Œ μ„œλ²„μ—μ„œλŠ” 이미 DB에 데이터가 μ €μž₯λ˜λ©΄μ„œ 데이터 뢈일치λ₯Ό μœ λ°œν•  수 μžˆλ‹€. Refer: https://alden-kang.tistory.com/20arrow-up-right

Last updated