日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
.NETCoreHttpClient請求異常分析

[[403870]]

本文轉(zhuǎn)載自微信公眾號「JeffckyShare」,作者Jeffcky 。轉(zhuǎn)載本文請聯(lián)系JeffckyShare公眾號。

最近項(xiàng)目上每天間斷性捕獲到HttpClient請求異常,感覺有點(diǎn)奇怪,于是乎觀察了兩三天,通過日志以及對接方溝通確認(rèn)等等,查看對應(yīng)版本源碼,嘗試添加部分配置發(fā)布后,觀察十幾小時(shí)暫無異常情況出現(xiàn),貌似問題已得到解決,若有后續(xù)繼續(xù)更新。

HttpClient來源:netstandard2.0

場景:將相關(guān)廠家地磁設(shè)備(停車進(jìn)出場)推送數(shù)據(jù),轉(zhuǎn)發(fā)至對接方。

最近一個(gè)星期經(jīng)過觀察會出現(xiàn)兩種異常情況,一種是請求連接操作被取消,另外一種則是請求處理過程中操作被取消,具體異常信息請見如下圖

我們知道HttpClient默認(rèn)超時(shí)時(shí)間為100s,但項(xiàng)目默認(rèn)設(shè)置請求超時(shí)時(shí)間為30s,初次分析異常情況來看,請求超時(shí)導(dǎo)致請求連接被取消異常,首先我telnet對接方端口通暢,于是乎與對接方交涉,是否存在從請求到對接方接口有額外前置處理,以及網(wǎng)絡(luò)是否存在波動等等,排查得知相關(guān)猜測都予以否決,網(wǎng)絡(luò)無任何問題

問題排查分析

既然網(wǎng)絡(luò)沒有任何問題,難道是對接方即服務(wù)端處理數(shù)據(jù)量巨大,導(dǎo)致請求應(yīng)答超時(shí)?于是乎對接方甩出幾張最近消息接收數(shù)據(jù)

從上述兩張圖來看,最多的一天也才90來萬,最近幾天請求失敗的數(shù)據(jù)大概2百來?xiàng)l,從我們平臺打印日志來看,每秒請求大致是10個(gè)左右,通過HTTP對接完全可以承載。

然后,因?yàn)槲覀儗?shù)據(jù)(JSON,數(shù)據(jù)大小幾乎可以忽略不計(jì))轉(zhuǎn)發(fā)到對接方,對接方拿到數(shù)據(jù)后不會進(jìn)行任何額外處理,直接存儲,所以我們請求超時(shí)時(shí)間30s,怎么會導(dǎo)致超時(shí)而引發(fā)異常呢?很奇怪

沒轍了,只能拿起終極武器,tcp抓包分析,重新學(xué)習(xí)了tcp/ip協(xié)議族一波

WireShark抓包分析

為打開分析上述pcap文件,提前安裝抓包軟件WireShark最新版本,由于軟件項(xiàng)目部署在Linux上,我們通過如下命令進(jìn)行抓包

 
 
 
 
  1. tcpdump -i any port 1443 -w exception.pcap 

從如下抓包信息可以直接知道,對接方使用了HTTPS協(xié)議,具體請看如下圖

如圖

默認(rèn)打開如上圖所示,其中time為時(shí)間戳,為找到我們指定時(shí)間點(diǎn)(2021-06-04 15:46:17.296),通過tab:視圖-時(shí)間顯示格式-日期和時(shí)間

接下來我們找到包文件中導(dǎo)致請求被取消異常的具體時(shí)間節(jié)點(diǎn)(2021-06-04 15:46:17.296)

在TCP協(xié)議中RST表示復(fù)位,用于異常時(shí)關(guān)閉連接。在發(fā)送RST包關(guān)閉連接時(shí),不必等待緩沖區(qū)的包都發(fā)出去,直接丟棄緩沖區(qū)的包而發(fā)送RST包。而接收端收到RST包后,也不必發(fā)送ACK包來確認(rèn)。

好像有點(diǎn)眉頭了,繼續(xù)往下看

好家伙,結(jié)合這張圖來看,基本上可以得出結(jié)論:原來是我們平臺主動重置了連接,緊接著又開始了多次進(jìn)行三次握手連接即(SYN、SYN/ACK、ACK)

示例代碼分析

推送邏輯是在類庫中使用HttpClient,所以沒有使用HttpClientFactory,因此定義靜態(tài)變量來使用HttpClient,而非每一個(gè)請求就實(shí)例化一個(gè)HttpClient

接下來我們來詳細(xì)分析項(xiàng)目示例代碼并對其進(jìn)行改進(jìn)

 
 
 
 
  1. static class Program 
  2.     static HttpClient httpClient = CreateHttpClient(); 
  3.     static Program() 
  4.     { 
  5.         ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 
  6.  
  7.         ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, error) => true, 
  8.     } 
  9.  
  10.     static async Task Main(string[] args) 
  11.     { 
  12.         await httpClient.PostAsync("", new StringContent("")); 
  13.     } 
  14.  
  15.     static HttpClient CreateHttpClient() 
  16.     { 
  17.         var client = new HttpClient(new HttpClientHandler 
  18.         { 
  19.             ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true 
  20.         }) 
  21.         { 
  22.             Timeout = TimeSpan.FromSeconds(30) 
  23.         }; 
  24.  
  25.         client.DefaultRequestHeaders.Accept.Clear(); 
  26.  
  27.         client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
  28.         client.DefaultRequestHeaders.Add("ContentType", "application/json"); 
  29.         return client; 
  30.     } 

若對接方僅使用HTTPS協(xié)議,無需驗(yàn)證證書,最好是忽略證書驗(yàn)證,否則有可能會引起建立驗(yàn)證證書連接異常,即添加

 
 
 
 
  1. ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true 

我們觀察上述代碼,有兩個(gè)地方都對證書驗(yàn)證進(jìn)行了設(shè)置,一個(gè)是在靜態(tài)構(gòu)造函數(shù)中ServicePointManager(簡稱SP),另外則在實(shí)例化HttpClient構(gòu)造函數(shù)中即HttpClientHandler(簡稱HCH),那么這二者是否有使用上的限制呢?

在.NET Framework中,內(nèi)置的HttpClient建立在HttpWebRequest之上,因此可以使用SP來配置

在.NET Core中,通過SP配置證書信息僅影響HttpWebRequest,而對HttpClient無效,需通過HCH配置來達(dá)到相同目的

所以去除在靜態(tài)構(gòu)造函數(shù)中對忽略證書的配置,改為在HttpClientHandler中

 
 
 
 
  1. var client = new HttpClient(new HttpClientHandler 
  2.     ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true, 
  3.     SslProtocols = SslProtocols.Tls12 
  4. }) 

回到本文的話題,為什么會重置連接即主動關(guān)閉連接呢?我們已分析過,和上述配置30s超時(shí)沒有關(guān)系,主要有兩方面原因

翻開并溫習(xí)《圖解HTTP》一書,如果請求頻繁,最好建立持久連接,減少TCP連接的重復(fù)建立和斷開所造成的額外開銷,從而減輕服務(wù)端負(fù)載即重用HTTP連接,也稱為Http keep-alive,持久連接的特點(diǎn)是,只要任意一方?jīng)]有明確提出斷開連接,否則保持TCP連接狀態(tài)

配置keep-alive我們俗稱為?;顧C(jī)制,所以在默認(rèn)請求頭中添加如下一行

 
 
 
 
  1. //增加?;顧C(jī)制,表明連接為長連接 
  2. client.DefaultRequestHeaders.Connection.Add("keep-alive"); 

上述只是在報(bào)文頭中添加持久化連接標(biāo)識,但不意味著就一定生效,因?yàn)槟J(rèn)是禁用持久化連接,所以為了保險(xiǎn)起見,添加如下代碼

 
 
 
 
  1. //啟用?;顧C(jī)制(保持活動超時(shí)設(shè)置為 2 小時(shí),并將保持活動間隔設(shè)置為 1 秒。) 
  2. ServicePointManager.SetTcpKeepAlive(true, 7200000, 1000); 

有個(gè)讓我很疑惑的問題,通過查看設(shè)置啟用持久化連接源碼得知,這樣設(shè)置意義在哪里?沒弄明白,源碼如下

 
 
 
 
  1. public static void SetTcpKeepAlive(bool enabled, int keepAliveTime, int keepAliveInterval) 
  2.     if (enabled) 
  3.     { 
  4.         if (keepAliveTime <= 0) 
  5.         { 
  6.             throw new ArgumentOutOfRangeException(nameof(keepAliveTime)); 
  7.         } 
  8.         if (keepAliveInterval <= 0) 
  9.         { 
  10.             throw new ArgumentOutOfRangeException(nameof(keepAliveInterval)); 
  11.         } 
  12.     } 

最關(guān)鍵的一點(diǎn)則是默認(rèn)持久化連接數(shù)為2,非持久化連接為4

 
 
 
 
  1. public class ServicePointManager 
  2.  { 
  3.      public const int DefaultNonPersistentConnectionLimit = 4; 
  4.      public const int DefaultPersistentConnectionLimit = 2; 
  5.  
  6.      private ServicePointManager() { } 
  7.  } 

那么問題是否就已很明了,項(xiàng)目中使用非持久化連接,即連接為4,未深究源碼具體細(xì)節(jié),大膽猜想一下,若連接大于4,是否會出現(xiàn)將此前連接主動關(guān)閉,重建新的連接請求呢?

最終我們講原始代碼修改為如下形式

 
 
 
 
  1. static class Program 
  2.     static HttpClient httpClient = CreateHttpClient(); 
  3.  
  4.     static Program() 
  5.     { 
  6.         //默認(rèn)連接數(shù)限制為2,增加連接數(shù)限制 
  7.         ServicePointManager.DefaultConnectionLimit = 512; 
  8.  
  9.         //啟用?;顧C(jī)制(保持活動超時(shí)設(shè)置為 2 小時(shí),并將保持活動間隔設(shè)置為 1 秒。) 
  10.         ServicePointManager.SetTcpKeepAlive(true, 7200000, 1000); 
  11.     } 
  12.  
  13.     static async Task Main(string[] args) 
  14.     { 
  15.         await httpClient.PostAsync("", new StringContent("")); 
  16.  
  17.         Console.WriteLine("Hello World!"); 
  18.     } 
  19.  
  20.     static HttpClient CreateHttpClient() 
  21.     { 
  22.         var client = new HttpClient(new HttpClientHandler 
  23.         { 
  24.             ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true, 
  25.             SslProtocols = SslProtocols.Tls12 
  26.         }) 
  27.         { 
  28.             Timeout = TimeSpan.FromSeconds(30) 
  29.         }; 
  30.  
  31.         client.DefaultRequestHeaders.Accept.Clear(); 
  32.         //增加?;顧C(jī)制,表明連接為長連接 
  33.         client.DefaultRequestHeaders.Connection.Add("keep-alive"); 
  34.         client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
  35.         client.DefaultRequestHeaders.Add("ContentType", "application/json"); 
  36.         return client; 
  37.     } 

進(jìn)行如上設(shè)置后,通過一天觀察,再未出現(xiàn)相關(guān)異常,至此問題解決告一段落,希望沒有后續(xù)......

強(qiáng)烈建議:利用HttpClient發(fā)送請求設(shè)置持久化連接和根據(jù)實(shí)際業(yè)務(wù)評估增加連接數(shù),而非默認(rèn)的持久化連接為2,非持久化連接為4,否則極易出現(xiàn)相關(guān)異常。

引發(fā)思考:利用HttpClientFactory創(chuàng)建HttpClient是否有默認(rèn)連接限制,據(jù)我所知,好像可以通過屬性MaxConnectionsPerServer來配置

若非常清楚默認(rèn)連接數(shù)限制,可能并算不上什么問題,也不存在如此諸多分析,不過對于我而言,收獲的是在問題排查過程中,對可能干擾信息的過濾、篩選、確認(rèn)以及對網(wǎng)絡(luò)協(xié)議進(jìn)一步的理解加深。

 

在.NET Core和.NET Framework中相關(guān)配置還是有些變化,最好是根據(jù)對應(yīng)版本在官網(wǎng)上確認(rèn)下

 


分享名稱:.NETCoreHttpClient請求異常分析
文章分享:http://m.5511xx.com/article/cdscsch.html