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