當你的程式需要大量的往另外一台機器打 http request 的時候,有很大的機會你也會中 TCP/IP Port Socket Exhaustion (Socket 耗盡) 的錯誤。
程式會噴 exception,並且內容是 System.Net.Sockets.SocketException: Address already in use 。
在 TCP/IP 通訊協定中,當一個連線被使用完關閉後,這個 Socket 釋放出的 Port 不會馬上變成可用的狀態,而是會進入TIME_WAIT 的狀態。在 Windows 預設環境下,如果沒有調整過 registery 設定的話,這個狀態會持續 240 秒。
所以你的程式在短時間內建立了大量的連線。例如:大量的 new HttpClient(),然後又再 dispose。那這些連線在關閉後都會卡在 TIME_WAIT 狀態 240 秒。然後當能用的 Port 全部都被佔滿的時候,而舊的連線都還沒從 TIME_WAIT 釋放時,新的連線就無法建立,接著就是中這個錯誤了。
如果不想去動到程式,那就是先大概算一下一台機器的 RPS 大概是多少,然後在 windows registry HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters 位置底下新增二個參數設定,型別選 REG_DWORD。
相關設定
個人經驗:
調整 Registry 可以起到立即緩解的作用,但嚴格來說算是「治標」。如果程式碼的實作是會不斷產生新的連線,再多的 Port 也會被耗盡。
將 HttpClient 宣告為靜態(Static)或是實作 Singleton。讓連線可以被重覆使用,避免每次都要重新建立連線。
缺點
個人經驗
在維護 .NET Framework 的舊專案時,如果發現量吃不高,較常遇到的都是 DefaultConnectionLimit 沒有調高,導致往外打的源頭在那邊慢慢打。
.net 後來再新出的一個類別,可以用來解決 Singleton 帶來的 DNS 問題,以及大量建立連線導致的 Socket 耗盡。
好處
個人經驗:
較新專案的實作直接都統一用 HttpClientFactory,因為簡單粗暴。如果是公司內部程式 server 的互打,並且打過來的 client 條件也允許的話,改用 gRPC 也是一個更好的選項。