隨着.net 5在11月的發佈,如今是談論網絡棧中許多改進的好時機。這包括對HTTP、套接字、與網絡相關的安全性和其餘網絡通訊的改進。在這篇文章中,我將重點介紹一些版本中更有影響力和更有趣的變化。ios
HTTPgit
更好的錯誤處理 github
自從.net 3.1發佈以來,HTTP領域進行了許多改進和修復。當使用HttpClien時,最受關注的是添加如何區分超時和取消。最初,不得不使用自定義的CancellationToken區分超時和取消:算法
這樣作,客戶端仍然拋出TaskCanceledException(爲了兼容),但內部異常是超時時的TimeoutException:編程
另外一個改進是將HttpStatusCode添加到HttpRequestException中。當響應上調用EnsureSuccessStatusCode時,新的StatusCode屬性能夠設置爲空。而後,它能夠在異常過濾器中使用:小程序
因爲HttpClient中方法:GetStringAsync, GetByteArrayAsync和GetStreamAsync不返回HttpResponseMessage,它們本身調用EnsureSuccessStatusCode。這些調用的異常過濾以下所示:後端
因爲新的構造函數是public的,因此能夠手動建立帶有狀態碼的HttpRequestException:api
一致的跨平臺實現瀏覽器
最初,.NET Core中的HTTP棧依賴於平臺相關的處理程序:安全
因爲兩個庫之間的差別,幾乎不可能實現跨平臺的一致性。所以,在.net Core 2.1中,咱們引入了一個名爲SocketsHttpHandler的託管HTTP實現。咱們將大部分工做轉移到SocketsHttpHandler,隨着咱們對它的可靠性愈來愈有信心,咱們決定徹底從System.Net.Http.dll中刪除特定於平臺的處理程序。在.net 5中,再也不可能使用切換回System.Net.Http。然而,WinHttpHandler仍然做爲一個獨立的NuGet包可用。任何使用它的代碼都須要更改成引用System.Net.Http.WinHttpHandler的NuGet包:
並顯式地向HttpClient構造函數傳遞WinHttpHandler實例:
SocketsHttpHandler擴展點
HttpClient是一個高級API,使用方便,但在某些狀況下缺少靈活性。在更高級的場景中,須要更精細的控制。咱們試圖彌合這些差距,並在SocketsHttpHandler中引入了兩個擴展點——ConnectCallback和PlaintextStreamFilter。
ConnectCallback容許自定義建立新鏈接。每次打開一個新的TCP鏈接時都會調用它。回調可用於創建進程內傳輸、控制DNS解析、控制基礎套接字的通用或特定於平臺的選項,或者僅用於在新鏈接打開時通知。回調有如下注意事項:
當不提供回調時的默認實現等價於如下最小的、基於套接字的回調:
另外一個擴展點,PlaintextStreamFilter,容許在新打開的鏈接上插入一個自定義層。在鏈接徹底創建以後(包括用於安全鏈接的TLS握手),但在發送任何HTTP請求以前調用此回調。所以,可使用它來監聽經過安全鏈接發送的純文本數據。這個回調的通常準則是:
如何實現自定義流能夠在文檔中找到。自定義流最終應該將讀寫任務委託給所提供的流,但它能夠攔截交換的數據。
一個很是小的沒有自定義流的PlaintextStreamFilter示例以下:
建立新擴展點鏈接的時間軸爲:
若是沒有註冊回調函數,這裏就不會調用任何東西。這兩個回調函數都是爲了對SocketsHttpHandler中的鏈接進行高級控制。應該很是當心地執行和測試它們,由於它們可能會無心中致使性能和穩定性問題。
HttpClient.Send的同步API
雖然咱們建議使用異步網絡API以得到更好的性能和可伸縮性,但咱們也認識到,在某些狀況下,使用同步API是必要的,而且會同步阻塞等待HttpClient。SendAsync常常有可伸縮性問題,由於須要多個線程來完成一個操做。這種方法的其餘缺陷,包括臭名昭著的UI線程死鎖。
爲了啓用同步場景並避免這些問題,咱們添加了一個同步版本的HttpClient.Send,可是實現有一些注意事項:
咱們強烈建議儘量繼續使用異步api。
HTTP / 2
版本選擇
這個特性是從支持明文HTTP/2 (h2c)的請求演變而來的。明文通訊不只適用於本地調試或測試環境,還可能存在防火牆或反向代理後的基於HTTP/2的服務,這些服務不使用TLS。例如,gRPC服務使用HTTP/2做爲傳輸協議,有些服務選擇放棄加密。
直到.net 5,一個不受支持的應用程序開關必須被打開才能啓用明文HTTP/2通訊,這可能會有問題,由於它不能啓用每一個請求控制。若是沒有交換機,每一個明文HTTP/2請求都會自動降級爲HTTP/1.1。這是由於TLS擴展ALPN被用於與服務器協商最終的HTTP版本。沒有TLS,所以沒有ALPN,客戶端不能肯定服務器將可以處理HTTP/2。所以,客戶端避免了風險,並選擇了廣泛支持的HTTP/1.1。可是,在前面提到的後端服務和gRPC的狀況下,可能事先就知道全部參與者均可以處理h2c,所以自動降級是不可取的。
當咱們設計版本選擇時,咱們試圖歸納原來的問題,並使API合理地「證實將來」。所以,咱們決定讓用戶控制如何處理版本的降級和升級。咱們引入了HttpVersionPolicy,這是一個新的enum,表示是否接受降級、升級,或者只接受準確的版本。用於手動建立並由HttpClient.SendAsync的發送。策略能夠經過HttpRequestMessage.VersionPolicy直接設置到請求。對於GetAsync、PostAsync、DeleteAsync等在內部建立請求的調用,HttpClient實例屬性HttpClient.DefaultVersionPolicy用於控制策略。
例如,要啓用h2c場景,咱們能夠這樣作:
與HTTP/2的多個鏈接
HTTP/2容許多個併發請求一個TCP鏈接上的多路傳輸。根據HTTP/2規範,只應該向服務器打開一個TCP鏈接。這個建議對於瀏覽器很是有效,而且解決了HTTP/1打開每一個源的多個鏈接的問題。然而,這將最大併發請求數減小到設置幀中的值,一般能夠設置爲100。對於服務到服務的通訊,其中一個客戶機向少許服務器發送很是多的請求,而且/或能夠保持多個長期存在的請求,這一限制會顯著影響吞吐量和性能。爲了克服這個限制,咱們引入了向單個端點打開多個HTTP/2鏈接的能力。
默認狀況下,多個HTTP/2鏈接是禁用的。要啓用它們,將SocketsHttpHandler.EnableMultipleHttp2Connections設置爲true。
多個併發請求的示例以下:
控制檯將顯示來自ConnectCallback關於建立新鏈接的多條消息。若是將EnableMultipleHttp2Connections註釋掉,控制檯將只顯示一條消息。
可配置的PING
HTTP/2規範定義了PING幀,這是一種確保空閒鏈接保持活躍的機制。此特性對於長時間運行的空閒鏈接很是有用,不然這些空閒鏈接將被刪除。這樣的鏈接能夠在gRPC場景中找到,好比流和長時間的遠程過程調用。到目前爲止,咱們只回復PING請求,從不發送。
在.net 5中,咱們已經實現了發送PING幀的可配置間隔、超時,以及是否老是或僅在活動請求時發送它們。默認值的配置是:
默認值KeepAlivePingDelay (Timeout.InfiniteTimeSpan)意味着該特性一般是關閉的,PING幀不會自動發送到服務器。客戶端仍然會回覆收到的PING幀,這是不能關閉的。爲了啓用自動PING, KeepAlivePingDelay必須更改,例如1分鐘:
只有當與服務器沒有主動通訊時才發送PING幀。每個來自服務器的傳入幀都將重置延遲,只有在KeepAlivePingDelay沒有接收到幀以後,纔會發送一個PING幀。而後,服務器被給予KeepAlivePingTimeout應答時間間隔。若是沒有,則認爲鏈接丟失並被拆除。該算法會按期檢查延遲和超時,但最多每秒鐘檢查一次。將KeepAlivePingDelay或KeepAlivePingTimeout設置爲更小的值將致使異常。
例如,設置以下:
將致使1.875秒間隔,由於它是兩個值的1/4,即min(KeepAlivePingDelay, KeepAlivePingTimeout)/4。在這種狀況下,超時可能發生在發送PING幀後的7.5到9.5秒之間。注意,檢查間隔的計算是一個實現細節,未來可能會更改。
HTTP / 3
HTTP/3及其底層傳輸層QUIC正處於標準化的最後階段。QUIC是一種新的基於udp的傳輸,與基於TCP的鏈接相比,它提供了一些好處:
-TLS安全連接握手更快
-在單個鏈接上更可靠的多路複用多個請求,消除了當數據包被丟棄時線路阻塞問題。
-鏈接遷移使移動客戶端網絡之間的轉換更加流暢,例如Wi-Fi到LTE再返回。
. net 5引入了對HTTP/3的實驗性支持——目前還不建議在生產環境中使用該特性。在底層,咱們使用的是MsQuic庫,它是一個開源的、跨平臺的QUIC協議實現。如何使用QUIC啓用HTTP/3的詳細說明能夠在System.Net.Experimental.MsQuic中找到。
這個設置告訴HttpClient咱們已經預先知道了服務器支持HTTP/3並請求它。若是不支持HTTP/3,則會拋出異常。另外一種選擇是讓服務器經過Alt-Svc報頭髮布HTTP/3。而後客戶端能夠將其用於後續請求。對於這個場景,請求不該該要求RequestVersionExact,由於它須要用較低的協議版本處理第一個請求。
更好的取消支持
基於Task的異步方法如今是異步編程的首選模式。它們在須要進行大量I/O操做的網絡中特別有價值。Task模式使代碼比原來的開始/結束「APM」模式更容易理解。基於Task的異步模式的一部分是使用CancellationToken來取消和超時。咱們一直在努力添加取消令牌,並將其正確地應用到各個地方。咱們仍然有遺漏重載的漏洞,但咱們已經在.net 5中填補了許多。
對於socket,咱們在SocketTaskExtensions中添加了重載——咱們想在.net 6中將它們放Socket類自己中。使用CancellationToken的新重載以下:
這些重載已經在HttpClient和TcpClient中使用,致使了TcpClient中的新的重載:
在HTTP命名空間中,咱們添加了HttpClient 和HttpContent 的重載。' HttpClient '被擴展爲' Get(ByteArray|Stream|String)Async '重載:
HttpContent添加了序列化和讀取的重載:
若是咱們如今設計HttpContent,使用全部重載,咱們將使CreateContentReadStreamAsync和SerializeToStreamAsync變爲abstract 而不是virtual。問題是咱們試圖不經過改變公共類的契約來破壞現有的代碼。在.net Core 3.1下運行的內容應該繼續在.net 5下運行,沒有任何改變。添加abstract 方法違背了這一承諾,因此咱們不得不求助於virtual方法。自定義HttpContent實現應該重寫它們,儘管它們只是virtual。全部HttpContent實現,好比byteraycontent、MultipartContent和StreamContent,都已經這樣作了。
網絡遙測
咱們已經意識到,用戶關於監視.net Core應用程序的內部網絡描述並很差。到目前爲止,只能收集很是詳細且不一致的日誌消息,偵聽它們對性能有影響。對於.net 5,咱們設計並實現了一套新的遙測事件和計數器。這些事件和計數器是在考慮持續監視的狀況下建立的,所以它們不像內部日誌那樣佔用大量資源。然而,它們並非徹底沒有代價的,監聽會消耗一些(儘管不多)CPU週期。
咱們正在公開這些新的遙測事件和計數器,它們將供.net用戶使用。咱們計劃在將來支持它們,對它們的任何更改都將被視爲突破性的更改。
遙測事件和計數器都基於EventSource。它們能夠經過EventListener在進程內使用,也能夠經過EventPipe經過dotnet-trace和dotnet-counters命令行工具在進程外使用。
自定義遙測事件
一種自定義遙測事件的方法是經過EventListener在進程中編程:
這個小程序將產生以下的控制檯日誌:
命名爲*Start和*Stop的事件使用相同的ActivityId觸發。這些事件具備特殊的意義並自動關聯。它容許像PerfView這樣的監視工具計算操做所消耗的時間,或將其餘事件連接到父事件。
另外一種方法是經過dotnet-trace進程以外:
計數器
計數器能夠經過EventListener在進程中以編程方式使用:
控制檯日誌是這樣的:
或進程外部的啓動計數器:
這將啓動計數器監視,用實際值覆蓋終端窗口,看起來相似:
. net中的安全層依賴於底層操做系統及其功能。
-對於基於Linux的系統,咱們使用OpenSSL,它從1.1.1版本起就支持TLS 1.3。
-對於Windows 10, TLS 1.3是可用的版本1903,但只用於測試目的,而不是生產。
此外,它是可選的,必須在註冊表中啓用。所以,TLS 1.3在以前的.net Core版本不能在Windows上工做。
這在內部預覽版中有所改變,其中TLS 1.3是默認開啓的,能夠經過新的API使用。咱們針對新的API調整了Windows上的SslStream實現,並在.net 5的Windows內部預覽版本中對其進行了測試。
咱們還追求在.net 5的SSL測試中得到A級。爲了實現這一點,咱們必須對Linux上的SslStream引入一個破壞性的更改,咱們如今設置了一個被認爲是強大的默認密碼套件的自覺得是的列表:
最後指出
本文並非咱們所作的全部更改的完整列表。若是你發現任何錯誤,請絕不猶豫地聯繫咱們,你能夠在dotnet/ncl別名下找到咱們。
歡迎關注個人公衆號,若是你有喜歡的外文技術文章,能夠經過公衆號留言推薦給我。
原文連接:https://devblogs.microsoft.com/dotnet/net-5-new-networking-improvements/