当前位置: 首页 > 科技观察

.NETCoreHttpClient请求异常分析

时间:2023-03-12 03:43:49 科技观察

本文转载自微信公众号《JeffckyShare》,作者Jeffcky。转载本文请联系JeffckyShare公众号。最近项目中每天断断续续的抓到HttpClient请求异常。感觉有点奇怪,于是观察了两三天,通过日志和与对接方的沟通,查看了对应版本的源码,尝试添加了一些配置。发布后,观察了十几个小时,没有任何异常情况,看来问题已经解决了。如果有后续,继续更新。HttpClient来源:netstandard2.0场景:从相关厂家的地磁设备(停车、出站)推送数据,转发给对接方。经过近一周的观察,会出现两个不正常的情况。一种是请求连接操作取消,另一种是请求处理过程中取消操作。具体异常信息见下图。我们知道HttpClient默认的超时时间是100s。但是项目默认设置请求超时时间为30s。根据异常情况初步分析,由于请求超时导致请求连接异常取消。首先我telnet了对接方的端口畅通无阻,于是和对接方协商,看是否从请求到对接方接口有问题。额外的预处理,以及是否有网络波动等,调查发现相关猜测被否决,网络没有问题。故障分析既然网络没有问题,是不是因为对接方也就是服务器正在处理海量数据,导致对请求有响应?时间到?于是对接方抛出了一些最近的消息接收数据。从上面两张图来看,最多的一天也就90万。这几天大概有200条数据请求失败。从我们的平台打印日志。你看,每秒大概有10个请求,可以通过HTTP连接进行。然后,因为我们把数据(JSON,数据大小几乎可以忽略不计)转发给对接方,对接方收到数据后不会做任何额外处理,直接存储,所以我们请求超时30s,如何它会导致超时吗?异常怎么办?没有退路才奇怪。只能拿起终极武器tcp抓包分析,重新学习tcp/ip协议族。一波WireShark抓包分析为了打开分析上述pcap文件,提前安装最新版本的抓包软件WireShark。由于软件项目部署在Linux上,我们使用如下命令抓包:tcpdump-ianyport1443-wexception.pcap从下面的抓包信息可以直接得知对接方使用的是HTTPS协议。具体请看下图,如上图所示。display,其中time是时间戳,我们指定时间点(2021-06-0415:46:17.296)来查找,通过tab:view-timedisplayformat-dateandtime接下来,我们发现请求是包文件中canceled异常的具体时间节点(2021-06-0415:46:17.296)在TCP协议中RST表示reset,用于异常发生时关闭连接。发送RST包关闭连接时,不需要等待缓冲区中的所有包发送完,直接丢弃缓冲区中的包,发送RST包。接收端收到RST包后,不需要发送ACK包确认。好像有点皱眉,继续看不起这家伙,结合这张图,我们基本可以得出一个结论:原来是我们的平台主动重置了连接,然后开启了多次三向握手连接(SYN,SYN/ACK,ACK)示例代码分析推送逻辑是使用类库中的HttpClient,所以没有使用HttpClientFactory,所以定义静态变量使用HttpClient,而不是每次请求都实例化一个HttpClient接下来我们分析一下中的项目示例详细代码并改进它("",newStringContent(""));}staticHttpClientCreateHttpClient(){varclient=newHttpClient(newHttpClientHandler{ServerCertificateCustomValidationCallback=(消息,证书,chain,error)=>true})ds{Timeout=TimeSpan.From30)};client.DefaultRequestHeaders.Accept.Clear();client.DefaultRequestHeaders.Accept.Add(newMediaTypeWithQualityHeaderValue("application/json"));客户端.DefaultRequestHeaders.Add("ContentType","application/json");returnclient;}}如果对接方只使用HTTPS协议,则不需要验证证书,证书验证最好忽略,否则可能导致建立验证证书连接异常,即添加ServerCertificateCustomValidationCallback=(message,cert,chain,error)=>true我们观察上面的代码,有两个地方设置了证书验证,一个是静态构造函数中的ServicePointManager(简称SP),另一个是实例化的HttpClient构造函数中的HttpClientHandler(简称HCH),那么这两个的使用有什么限制吗?在.NETFramework中,内置的HttpClient是建立在HttpWebRequest之上的,所以你可以在.NETCore中使用SP配置,通过SP配置证书信息只影响HttpWebRequest,对HttpClient无效,同样需要配置HCH,所以去掉配置of忽略静态构造函数中的证书,将其更改为varclient=newHttpClient(newHttpClientHandler{ServerCertificateCustomValidationCallback=(message,cert,chain,error)=>inHttpClientHandlertrue,SslProtocols=SslProtocols.Tls12})回到本文的主题,为什么会重置连接,即主动关闭连接?我们分析过,与上面配置的30s超时无关。主要有两个原因。打开复习《图解HTTP》书,如果请求频繁,最好建立持久连接,减少TCP连接反复建立和断开带来的额外开销,从而减轻服务器的负载,即复用HTTP连接,也称为Httpkeep-alive,持久连接的特点是只要双方都没有明确提出断开连接,否则会保持TCP连接状态。配置keep-alive机制,也就是俗称的keep-alive机制,所以在默认的请求头中添加如下一行//添加keep-alive机制,表示该连接是长连接client.DefaultRequestHeaders.Connection.Add("保持活动状态");上面只是在消息头添加了持久连接标识,并不代表会生效,因为默认就是禁用持久连接,所以为了保险起见,添加如下代码//启用保活机制(设置保活超时时间为2小时,保活间隔时间设置为1秒)ServicePointManager.SetTcpKeepAlive(真,7200000,1000);有一个问题让我很困惑。我通过检查设置的源代码来了解启用持久连接。这个设置是什么意思?我不明白。源码如下)));}}}最重要的一点是默认持久连接数为2,非持久连接数为4publicclassServicePointManager{publicconstintDefaultNonPersistentConnectionLimit=4;publicconstintDefaultPersistentConnectionLimit=2;privateServicePointManager(){}}那么问题就已经很清楚了,项目使用非持久连接,即连接为4,没有深究源码的具体细节,大胆猜测,如果连接大于4,是否会主动关闭之前的连接并将重建一个新的连接请求?最后,我们将原来的代码修改为如下形式staticclassProgram{staticHttpClienthttpClient=CreateHttpClient();staticProgram(){//默认连接数限制为2,增加连接限制ServicePointManager.DefaultConnectionLimit=512;//启用keep-alive机制(设置keep-alive超时时间为2小时,设置keep-alive间隔为1秒。)ServicePointManager.SetTcpKeepAlive(true,7200000,1000);}staticasyncTaskMain(string[]args){awaithttpClient.PostAsync("",newStringContent(""));Console.WriteLine("HelloWorld!");}staticHttpClientCreateHttpClient(){varclient=newHttpClient(newHttpClientHandler{ServerCertificateCustomValidationCallback=(message,cert,chain,error)=>true,SslProtocols=SslProtocols.Tls12}){Timeout=TimeSpan.FromSeconds(30)};client.DefaultRequestHeaders.Accept.Clear();//添加keep-alive机制,表示该连接为长连接.DefaultRequestHeaders.Add("ContentType","application/json");returnclient;}}经过以上设置,经过一天的观察,没有出现相关的异常。至此,问题已经解决。希望不要有后续……强烈建议:使用HttpClient发送请求设置长连接,根据实际业务评估增加连接数,而不是默认长连接2个,非长连接4个-持久连接,否则很容易出现相关异常。思考:使用HttpClientFactory创建HttpClient是否有默认的连接数限制?据我所知,好像可以通过属性MaxConnectionsPerServer来配置。就我而言,收获的是在排查过程中对可能存在的干扰信息的过滤、筛选、确认以及对网络协议的更深入的理解。.NETCore和.NETFramework中的相关配置还有一些变化,最好根据对应的版本到官网确认