.NET Core HttpClient请求异常分析

推送逻辑是在类库中使用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
})

配置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;
    }
}
相关推荐
IT规划师15 小时前
C#|.net core 基础 - 扩展数组添加删除性能最好的方法
c#·.netcore·数组
时光追逐者16 小时前
分享6个.NET开源的AI和LLM相关项目框架
人工智能·microsoft·ai·c#·.net·.netcore
Lingbug1 天前
.Net日志组件之NLog的使用和配置
后端·c#·.net·.netcore
IT规划师2 天前
C#|.net core 基础 - 值传递 vs 引用传递
c#·.netcore·值传递·引用传递
wenyinvjiuke2 天前
节日庆典中的白酒文化,传承与创新并存
.netcore
平凡而伟大(心之所向)4 天前
关于.net Framework向.net core的移植
java·.net·.netcore
时光追逐者4 天前
C#/.NET/.NET Core技术前沿周刊 | 第 5 期(2024年9.9-9.15)
microsoft·c#·.net·.netcore
世界太过浮夸7 天前
.net core 通过Sqlsugar生成实体
.netcore