HttpClient参观记:.net core 2.2 对HttpClient到底做了什么?
.net core 于 10月17日發布了?ASP.NET Core 2.2.0 -preview3,在這個版本中,我看到了一個很讓我驚喜的新特性:HTTP Client Performance Improvements?,而且在Linux上性能提升了60% !
之前就一直苦于 HttpClient 的糟糕特性,大家耳熟能詳的?You are using HttpClient wrong。
因為 HttpClient 實現了?IDisposable?如果用完就釋放,Tcp 連接也會被斷開,并且一個HttpClient 通常會建立很多個 Tcp 連接 。 Tcp 連接斷開的過程是有一個 Time_Wait 狀態的,因為要保證 Tcp 連接能夠斷開,以及防止斷開過程中還有數據包在傳送。這本身沒有毛病,但是如果你在使用 HttpClient 后就將其注銷,并且同時處于高并發的情況下,那么你的 Time_Wait 狀態的 Tcp 連接就會爆炸的增長,
他們占用端口和資源而且還遲遲不消失,就像是在 嘲諷 你。所以臨時解決方式是使用靜態的 HttpClient 對象,No Dispose No Time_Wait
后來在 .net core2.1 中,引入了?HttpClientFactory?來解決這一問題。 HttpClientFactory 直接負責給 HttpClient 輸入 全新的?HttpMessageHandle?對象,并且管理 HttpMessageHandle 的生殺大權,這樣斷開 Tcp 連接的操作都由 HttpClientFactory 來用一種良好的機制去解決。
上面說了一堆,其實和主題關系不大。 因為我在實際生產環境中,無論使用靜態的 HttpClient 還是使用 HttpClientFactory ,在高并發下的情況下 Tcp 連接都陡然上升。直到我將 .net core 2.1 升級到 .net core 2.2 preview 問題似乎奇跡般的解決了。在介紹 .net core 2.2 如何提升 HttpClient 性能的時候,需要先簡單介紹下 HttpClient :
上面說到了 HttpMessageHandle ( 顧名思義:Http消息處理器 ) 它是一個抽象類,用來干嘛的呢? 處理請求,又是顧名思義。 HttpClient 的發送請求函數 :SendAsync()
? public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption,CancellationToken cancellationToken){....}最后調用的就是 HttpMessageHandle 的 SendAsync 抽象函數。
事實上通過閱讀源碼發現,幾乎所有繼承 HttpMessageHandle 的子類都有一個 HttpMessageHandle 類型的屬性 :?_handle,而每個子類的 SendAsync 函數都調用 _handle 的 SendAsync()。我們知道在初始化一個 HttpClient 的時候或者使用 HttpClientFactory 創建一個HttpClient 的時候都需要新建 或者傳入一個 HttpMessageHandle 我把它叫做起始消息處理器。 很容易想像,HttpClient 的 SendAsync 函數是 一個 HttpMessageHandle 調用 下一個 HttpMessageHanlde 的SendAsync,而下一個 HttpMessageHandle 的SendAsync 是調用下下一個HttpMessageHandle 的 SendAsync 函數。每一個HttpMessageHandle 都有其自己的職責。
層層嵌套,環環相扣,循環往復,生生不息,額不對,這樣下去會死循環。 直到它到達終點,也就是Tcp 連接建立,拋棄回收,發送請求的地方。 所以 HttpClient 的核心 就是由這些 HttpMessageHandle 扣起來,打造成一個 消息通道。 每個請求都無一例外的 通過這個通道,找到它們的最終歸宿。
這其中的順序到底是啥,我并不關心,我只關心其中一個 環:SocketsHttpHandle 因為.net core 2.2 就是從這個環開始動了手術刀,怎么動的,按照上面的說法,我們從 SocketHttpHandle 開始順藤摸瓜。其實顧名思義 SocketsHttpHandle 已經很接近 HttpClient 的通道的末尾了。這是 摸出來的 鏈條 :
SocketsHttpHandle?---->?HttpConnectionHandler/HttpAuthenticatedConnectionHandler?---->?HttpConnectionPoolManager?---->?HttpConnectionPoolManager
--->?HttpConnectionPool
最后一個加粗是有原因的,因為我們摸到尾巴了,HttpConnectionPool( 顧名思義 Http 連接 池) 已經不繼承 HttpMessageHandle 了 ,它就是我們要找的終極,也是請求最終獲取連接的地方,也是.net core 2.2 在這條鏈中的 操刀的地方。
接下來就要隆重介紹 手術過程。手術的位置在哪里? 就是獲取 Tcp 連接的函數。我們看手術前的樣子,也就是System.Net.Http 4.3.3 版本的樣子。
整個過程一目了然,list?是存放 閑置的Tcp連接 的鏈表,當一個?請求?千辛萬苦到了這里,它要開始在鏈表的末尾開始 查找有沒有可以用的?小跑車(Tcp連接),先把從小跑車 從?車庫(list)里搬出來,然后檢查下動力系統,輪子啥的,如果發現壞了( 當前連接不可用 ,已經被服務端關閉的,或者有異常數據的 等等 ), 你需要用把這個壞的車給砸了( 銷毀Tcp連接 ),再去搬下一個小跑車。
如果可以用,那么很幸運,這個請求可以立刻開著小跑車去飆車(發送數據)。如果這個車庫的車全是壞的或者一個車都沒有,那么這個請求就要自己造一個小跑車 ( 建立新的TCP 連接 )。 這里還有一個點,小跑車數量是有限制的。假如輪到你了,你發現車庫里沒有車,你要造新車,但是系統顯示車子數量已經達到最大限制了,所以你就要等 小伙伴 ( 別的請求 ) 把 小跑車用完后開回來,或者等車庫里的壞車 被別的小伙伴砸了。
整個過程看起來好像也挺高效的,但是請注意?lock (SyncObj)?上述所有操作的都被上鎖了,這些操作同時只能有一個小伙伴操作,這樣做的原因當然是為了安全,防止兩個請求同時用了同一個Tcp連接,這樣的話車子會被擠壞掉的。 于是小伙伴們都一個一個的排著隊。 試想,當我們的請求很多很多的時候,隊伍很長很長,那每個請求執行的時間久會變長。
那有沒有什么方法可以加快速度呢? 其實是有的,事實上危險的操作 只是從 list 中去取車,和造新車。防止搶車和兩個小伙伴造了同一個車。于是手術后的樣子是這樣的:
可以看出,它把加鎖執行的內容減少了,將檢查車子的工作放到鎖外。此外 將 lock...while 變成了while...lock 這樣有什么影響呢:可以減少線程之間的競爭,如評論所說,lock...while 是霸道的,一線程阻塞,萬線程等待競爭,而 while...lock 所有線程展開公平的競爭,大家持有鎖幾乎是相同的幾率。
沒想到這樣一個操作,在Linux中提升了60% 的性能。減少了小伙伴之間的等待時間。
那么 靜態的HttpClient 和 HttpClientFactory 的二者使用,哪個性能更好呢? 我認為是前者,在高并發的實驗過程中也確實如此。因為 靜態HttpClient 只有一個消息通道,從頭用到尾,這樣無疑是最高效的。而HttpClientFactory 需要銷毀 HttpMessageHandle 銷毀 HttpMessageHanlde 的過程是鏈條中的節點一個一個被摧毀的過程,直到最后的Tcp 連接池也被銷毀。在使用Service.AddHttpClient 時需要設置生存周期,這就是HttpMessageHandle 的生存時長,我認為應該將其設置的長一些,這樣HttpMessageHandle 或者叫做消息通道 就可以多多的被重復利用,因為HttpClientFactory 可以給不同的HttpClient注入相同的HttpMessageHandle
看完這篇文章 還可以看下這篇文章的姊妹篇:工廠參觀記:.NET Core 中 HttpClientFactory 如何解決 HttpClient 臭名昭著的問題
當然我遇到的問題 是否真的是因為 HttpClient 性能的提升而解決,現在也不能確定。還需要進一步檢測驗證。
相關文章:
工廠參觀記:.NET Core 中 HttpClientFactory 如何解決 HttpClient 臭名昭著的問題
.NET Core 中正確使用 HttpClient 的姿勢
原文地址:https://www.cnblogs.com/dacc123/p/9892274.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的HttpClient参观记:.net core 2.2 对HttpClient到底做了什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 全面支持开源,微软加速 Visual S
- 下一篇: 2018年10月28日宁波dotnet社