iT邦幫忙

1

筆記:高併發下的 http 地雷,TCP/IP Socket Exhaustion

  • 分享至 

  • xImage
  •  

筆記:高併發下的 http 地雷,TCP/IP Socket Exhaustion

當你的程式需要大量的往另外一台機器打 http request 的時候,有很大的機會你也會中 TCP/IP Port Socket Exhaustion (Socket 耗盡) 的錯誤。


現象

程式會噴 exception,並且內容是 System.Net.Sockets.SocketException: Address already in use

Root-Cause

在 TCP/IP 通訊協定中,當一個連線被使用完關閉後,這個 Socket 釋放出的 Port 不會馬上變成可用的狀態,而是會進入TIME_WAIT 的狀態。在 Windows 預設環境下,如果沒有調整過 registery 設定的話,這個狀態會持續 240 秒。

所以你的程式在短時間內建立了大量的連線。例如:大量的 new HttpClient(),然後又再 dispose。那這些連線在關閉後都會卡在 TIME_WAIT 狀態 240 秒。然後當能用的 Port 全部都被佔滿的時候,而舊的連線都還沒從 TIME_WAIT 釋放時,新的連線就無法建立,接著就是中這個錯誤了。


簡單解法:調整 Windows Registry

如果不想去動到程式,那就是先大概算一下一台機器的 RPS 大概是多少,然後在 windows registry HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters 位置底下新增二個參數設定,型別選 REG_DWORD。

  • 相關設定

    • TcpTimedWaitDelay: 縮短 Socket 處於 TIME_WAIT 的秒數時間,可設定成 30。
    • MaxUserPort: 增加系統可用的動態連接埠上限,可設成 60000。實際上有些是系統預設會使用的 port,也沒辦法搶走,設到 65535 也是沒什麼用。
  • 個人經驗:

調整 Registry 可以起到立即緩解的作用,但嚴格來說算是「治標」。如果程式碼的實作是會不斷產生新的連線,再多的 Port 也會被耗盡。


HttpClient 改用 static or singleton

HttpClient 宣告為靜態(Static)或是實作 Singleton。讓連線可以被重覆使用,避免每次都要重新建立連線。

  • 缺點

    • HttpClient 在建立連線後會記住 DNS 解析完的 IP。如果 DNS 進行了切換,static or singleton 的 HttpClient 不會去重新解析 DNS,會一直打到舊的 IP。
    • 在舊的 .net framework 環境裡,DefaultConnectionLimit 也要記得調整,不然 concurrent 往外打的數量預設就只有 2,一樣會被卡住。(.net core 就沒這問題)
  • 個人經驗

在維護 .NET Framework 的舊專案時,如果發現量吃不高,較常遇到的都是 DefaultConnectionLimit 沒有調高,導致往外打的源頭在那邊慢慢打。


改用 HttpClientFactory

.net 後來再新出的一個類別,可以用來解決 Singleton 帶來的 DNS 問題,以及大量建立連線導致的 Socket 耗盡。

  • 好處

    • HttpMessageHandler Pool:可以重覆使用 HttpMessageHandler,來減少頻繁建立 TCP 連線的成本。
    • 定期過期:預設每 2 分鐘會讓 Handler 過期並重新建立。這可以讓 Handler 重新建連線時,會重新解析一次 DNS 的 IP,可解決 Singleton 不會更新 DNS 的問題。
  • 個人經驗:

較新專案的實作直接都統一用 HttpClientFactory,因為簡單粗暴。如果是公司內部程式 server 的互打,並且打過來的 client 條件也允許的話,改用 gRPC 也是一個更好的選項。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言