.net framework 及 .net 8 HttpClient 之特殊案例探討
背景
此專案是一個舊系統升級案,舊系統的 .net 版本為 .net framework 4.8,要升級到 .net 8.
因此,基本原則是盡量不異動舊系統程式邏輯的前提下,將系統升級.升級的過程中,除了部分必要的基礎架構的異動外,
大致都順利完成升級了.
所以最後階段就是要進行新系統的壓力測試,測試結果不如預期,在八台 server 總共同時 1000 人的情境下,測試了 30 分鐘,
大約會出現 5% 的 request 發生了 HttpClient SendAsync 錯誤,錯誤訊息為:
System.Net.Http.HttpRequestException: An error occured while sending the request. Response ended prematurely. (ResponseEnded)
即使將壓測人數及 server 降低到兩台,同時 300 人,也一樣會出現錯誤
但是在相同條件下以舊系統進行壓力測試,完全沒有錯誤
API 測試的流程如下圖:
處理過程
由上面的錯誤訊息去 google 或是詢問 AI 助理,得到的答案不外乎都是這個要求被 server 端拒絕了,
所以這就不是我們的責任了,應該要去詢問“資料後台”為什麼會拒絕這個請求,但是實際在“資料後台”中,沒有發現有幫助的 Log 紀錄
另外一個比較難解釋的點是,舊中台完全沒有錯誤,為什麼新中台會持續地出現錯誤.
為了還原這種情境,我在公司的環境中,架設了一台跟客戶環境類似的“模擬資料後台”,用了三台 server ,總共 500 人的情境下去做壓力測試
都完全正常,沒有出現一樣的 HttpClient 錯誤資訊
為了釐清這個問題,反覆做了程式調整及無數次的壓力測試,依然解決不了,就這樣拖了一段時間
最後,在客戶端測試時,側錄了 TCP 封包的資料,得到了一些資訊:
361012 -> Client建立連線(SYN)
361016 -> Server回應連線建立(SYN, ACK)
361017 -> Client回應連線建立(ACK)
361065 -> Client傳送資料(PSH, ACK)
361191 -> Server回應資料(PSH, ACK)
361194 -> Client回應(ACK)
361266 -> Client傳送資料(PSH, ACK)
361606 -> Server回應資料(PSH, ACK)
361619 -> Server回應資料(ACK)
361620 -> Client回應(ACK)
361621、362290 -> Server回應資料(ACK)
362291 -> Client回應(ACK)
362292 -> Server回應資料並通知結束連線(FIN, PSH, ACK)
362294 ->Client回應(ACK)
362323 -> Client傳送資料(PSH, ACK)
362328、362550(重送) -> Client回應連線結束(FIN, ACK)
362969 -> Server回應Reset(RST, ACK)
362972、363091 -> Server回應Reset (RST)
網路這方面我不熟悉,所以我將上面的封包資料詢問我的“助理”,想看看他有什麼想法.經過了兩三回的詢問後,我得到了幾種可能的因素,
有一點我覺得有解決問題的機會,如下:
HttpClient 使用共享的連線池來進行 HTTP 請求。如果多個請求共享連線池並且超過了伺服器的允許併發數量,可能會導致擁塞和重試。調整 MaxConnectionsPerServer 可以限制每個伺服器的併發連接數,從而防止過多的重送
但是上述的說明無法解釋新舊系統的測試結果不同,但是我得到了一些靈感,於是我去查詢了 .net framework 4.8 及 .net 8 的 HttpClient MaxConnectionsPerServer 程式預設值是多少.
得到的結果有非常大的差距:
.net framework 4.8 預設值為 “2”
.net 8 預設值為 Int.Max(也就是 20億)
所以我們就將新系統的 MaxConnectionsPerServer 改成了 2 或 10,再進行壓力測試就完全沒有發生錯誤了
最後就來探討為什麼會有這種問題
在公司環境進行測試時,我所架設的”模擬資料後台“的程式框架為 .net 8
在客戶端的環境中,他們的“資料後台”為 Java 寫的服務,因為舊系統是在五年前就上線了,因此我猜測他們的“資料後台”也還是維持當初上線的程式版本,
沒有進行升級
而我也有提供給客戶我寫的“模擬資料後台”程式,請他們放到相同的 server 建置起來,壓力測試後,沒有發生相同的 HttpClient 錯誤
所以,應該是客戶的“資料後台”的版本太舊了,無法承受同時大量的連線,因此部分的請求被拒絕了
.net 在這幾年中進行了好幾次的升級,升級的核心有個重點是在對於高併發的處理,因此在 .net 8 的版本中,他可以將同時連線數的預設值放大到 20 億
代表他能夠承受非常大量的同時請求,這也是為什麼在我自己架設的環境中,一直無法重現相同的錯誤
在 Java 這幾年中,我認為也有進行了類似的升級,只是客戶端還是維持著舊版本
結論
如果要讓新系統發揮全部的效能,除了本身的專案系統要進行升級外,搭配的其他系統最好也要同步進行升級,才能發揮最大的效益,避免一些未預期的潛在錯誤