有关.NET Core HttpClient请求异常的问题

发布时间:2021-06-15 15:22:41 作者:chen
来源:亿速云 阅读:142

这篇文章主要讲解了“有关.NET Core HttpClient请求异常的问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“有关.NET Core HttpClient请求异常的问题”吧!

场景:将相关厂家地磁设备(停车进出场)推送数据,转发至对接方。

最近一个星期经过观察会出现两种异常情况,一种是请求连接操作被取消,另外一种则是请求处理过程中操作被取消,具体异常信息请见如下图

有关.NET Core HttpClient请求异常的问题

有关.NET Core HttpClient请求异常的问题

我们知道HttpClient默认超时时间为100s,但项目默认设置请求超时时间为30s,初次分析异常情况来看,请求超时导致请求连接被取消异常,首先我telnet对接方端口通畅,于是乎与对接方交涉,是否存在从请求到对接方接口有额外前置处理,以及网络是否存在波动等等,排查得知相关猜测都予以否决,网络无任何问题

问题排查分析

既然网络没有任何问题,难道是对接方即服务端处理数据量巨大,导致请求应答超时?于是乎对接方甩出几张最近消息接收数据

有关.NET Core HttpClient请求异常的问题

有关.NET Core HttpClient请求异常的问题

从上述两张图来看,最多的一天也才90来万,最近几天请求失败的数据大概2百来条,从我们平台打印日志来看,每秒请求大致是10个左右,通过HTTP对接完全可以承载。

然后,因为我们将数据(JSON,数据大小几乎可以忽略不计)转发到对接方,对接方拿到数据后不会进行任何额外处理,直接存储,所以我们请求超时时间30s,怎么会导致超时而引发异常呢?很奇怪

没辙了,只能拿起终极武器,tcp抓包分析,重新学习了tcp/ip协议族一波

WireShark抓包分析

为打开分析上述pcap文件,提前安装抓包软件WireShark最新版本,由于软件项目部署在Linux上,我们通过如下命令进行抓包

tcpdump -i any port 1443 -w exception.pcap

有关.NET Core HttpClient请求异常的问题

从如下抓包信息可以直接知道,对接方使用了HTTPS协议,具体请看如下图

有关.NET Core HttpClient请求异常的问题

如图

默认打开如上图所示,其中time为时间戳,为找到我们指定时间点(2021-06-04  15:46:17.296),通过tab:视图-时间显示格式-日期和时间

有关.NET Core HttpClient请求异常的问题

接下来我们找到包文件中导致请求被取消异常的具体时间节点(2021-06-04 15:46:17.296)

有关.NET Core HttpClient请求异常的问题

在TCP协议中RST表示复位,用于异常时关闭连接。在发送RST包关闭连接时,不必等待缓冲区的包都发出去,直接丢弃缓冲区的包而发送RST包。而接收端收到RST包后,也不必发送ACK包来确认。

好像有点眉头了,继续往下看

有关.NET Core HttpClient请求异常的问题

好家伙,结合这张图来看,基本上可以得出结论:原来是我们平台主动重置了连接,紧接着又开始了多次进行三次握手连接即(SYN、SYN/ACK、ACK)

示例代码分析

推送逻辑是在类库中使用HttpClient,所以没有使用HttpClientFactory,因此定义静态变量来使用HttpClient,而非每一个请求就实例化一个HttpClient

接下来我们来详细分析项目示例代码并对其进行改进

static class Program {     static HttpClient httpClient = CreateHttpClient();     static Program()     {         ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;          ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, error) => true,     }      static async Task Main(string[] args)     {         await httpClient.PostAsync("", new StringContent(""));     }      static HttpClient CreateHttpClient()     {         var client = new HttpClient(new HttpClientHandler         {             ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true         })         {             Timeout = TimeSpan.FromSeconds(30)         };          client.DefaultRequestHeaders.Accept.Clear();          client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));         client.DefaultRequestHeaders.Add("ContentType", "application/json");         return client;     } }

若对接方仅使用HTTPS协议,无需验证证书,最好是忽略证书验证,否则有可能会引起建立验证证书连接异常,即添加

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

我们观察上述代码,有两个地方都对证书验证进行了设置,一个是在静态构造函数中ServicePointManager(简称SP),另外则在实例化HttpClient构造函数中即HttpClientHandler(简称HCH),那么这二者是否有使用上的限制呢?

在.NET Framework中,内置的HttpClient建立在HttpWebRequest之上,因此可以使用SP来配置

在.NET Core中,通过SP配置证书信息仅影响HttpWebRequest,而对HttpClient无效,需通过HCH配置来达到相同目的

所以去除在静态构造函数中对忽略证书的配置,改为在HttpClientHandler中

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

回到本文的话题,为什么会重置连接即主动关闭连接呢?我们已分析过,和上述配置30s超时没有关系,主要有两方面原因

翻开并温习《图解HTTP》一书,如果请求频繁,最好建立持久连接,减少TCP连接的重复建立和断开所造成的额外开销,从而减轻服务端负载即重用HTTP连接,也称为Http  keep-alive,持久连接的特点是,只要任意一方没有明确提出断开连接,否则保持TCP连接状态

配置keep-alive我们俗称为保活机制,所以在默认请求头中添加如下一行

//增加保活机制,表明连接为长连接 client.DefaultRequestHeaders.Connection.Add("keep-alive");

上述只是在报文头中添加持久化连接标识,但不意味着就一定生效,因为默认是禁用持久化连接,所以为了保险起见,添加如下代码

//启用保活机制(保持活动超时设置为 2 小时,并将保持活动间隔设置为 1 秒。) ServicePointManager.SetTcpKeepAlive(true, 7200000, 1000);

有个让我很疑惑的问题,通过查看设置启用持久化连接源码得知,这样设置意义在哪里?没弄明白,源码如下

public static void SetTcpKeepAlive(bool enabled, int keepAliveTime, int keepAliveInterval) {     if (enabled)     {         if (keepAliveTime <= 0)         {             throw new ArgumentOutOfRangeException(nameof(keepAliveTime));         }         if (keepAliveInterval <= 0)         {             throw new ArgumentOutOfRangeException(nameof(keepAliveInterval));         }     } }

最关键的一点则是默认持久化连接数为2,非持久化连接为4

public class ServicePointManager  {      public const int DefaultNonPersistentConnectionLimit = 4;      public const int DefaultPersistentConnectionLimit = 2;       private ServicePointManager() { }  }

那么问题是否就已很明了,项目中使用非持久化连接,即连接为4,未深究源码具体细节,大胆猜想一下,若连接大于4,是否会出现将此前连接主动关闭,重建新的连接请求呢?

最终我们讲原始代码修改为如下形式

static class Program {     static HttpClient httpClient = CreateHttpClient();      static Program()     {         //默认连接数限制为2,增加连接数限制         ServicePointManager.DefaultConnectionLimit = 512;          //启用保活机制(保持活动超时设置为 2 小时,并将保持活动间隔设置为 1 秒。)         ServicePointManager.SetTcpKeepAlive(true, 7200000, 1000);     }      static async Task Main(string[] args)     {         await httpClient.PostAsync("", new StringContent(""));          Console.WriteLine("Hello World!");     }      static HttpClient CreateHttpClient()     {         var client = new HttpClient(new HttpClientHandler         {             ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,             SslProtocols = SslProtocols.Tls12         })         {             Timeout = TimeSpan.FromSeconds(30)         };          client.DefaultRequestHeaders.Accept.Clear();         //增加保活机制,表明连接为长连接         client.DefaultRequestHeaders.Connection.Add("keep-alive");         client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));         client.DefaultRequestHeaders.Add("ContentType", "application/json");         return client;     } }

进行如上设置后,通过一天观察,再未出现相关异常,至此问题解决告一段落,希望没有后续......

强烈建议:利用HttpClient发送请求设置持久化连接和根据实际业务评估增加连接数,而非默认的持久化连接为2,非持久化连接为4,否则极易出现相关异常。

引发思考:利用HttpClientFactory创建HttpClient是否有默认连接限制,据我所知,好像可以通过属性MaxConnectionsPerServer来配置

若非常清楚默认连接数限制,可能并算不上什么问题,也不存在如此诸多分析,不过对于我而言,收获的是在问题排查过程中,对可能干扰信息的过滤、筛选、确认以及对网络协议进一步的理解加深。

在.NET Core和.NET Framework中相关配置还是有些变化,最好是根据对应版本在官网上确认下

感谢各位的阅读,以上就是“有关.NET Core HttpClient请求异常的问题”的内容了,经过本文的学习后,相信大家对有关.NET Core HttpClient请求异常的问题这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

推荐阅读:
  1. 如何使用.NET CORE HttpClient
  2. .NET CORE HttpClient是怎么用的

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

上一篇:Java中如何使用 Comparator比较器

下一篇:Java中原子变量类的原理是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》