.net的 http 请求 利用单例减少创建销毁的资源开销 提高利用率
参照腾讯云sdk 链接 https://github.com/TencentCloud/tencentcloud-sdk-dotnet/blob/master/TencentCloud/Common/Http/HttpConnection.cs
减小创建的开销可以用单例,也可以用请求池,后续再简单写个请求池,这次主要贴下腾讯的代码,照着腾讯sdk的请求方式 可以直接搬到自己的项目中去使用 代码就不一一解释了,很容易看明白
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; namespace TencentCloud.Common.Http { public class HttpConnection { private class HttpClientHolder { private static readonly ConcurrentDictionary<string, HttpClientHolder> httpclients = new ConcurrentDictionary<string, HttpClientHolder>(); public static HttpClient GetClient(string proxy) { string key = string.IsNullOrEmpty(proxy) ? "" : proxy; HttpClientHolder result = httpclients.GetOrAdd(key, (k) => { return new HttpClientHolder(k); }); TimeSpan timeSpan = DateTime.Now - result.createTime; // 每5分钟创建一个新的连接,放弃旧连接,避免DNS刷新问题。 while (timeSpan.TotalSeconds > 300) { ICollection<KeyValuePair<string, HttpClientHolder>> kv = httpclients; kv.Remove(new KeyValuePair<string, HttpClientHolder>(key, result)); result = httpclients.GetOrAdd(key, (k) => { return new HttpClientHolder(k); }); timeSpan = DateTime.Now - result.createTime; } return result.client; } public readonly HttpClient client; public readonly DateTime createTime; public HttpClientHolder(string proxy) { string p = string.IsNullOrEmpty(proxy) ? "" : proxy; if (p == "") { this.client = new HttpClient(); } else { var httpClientHandler = new HttpClientHandler { Proxy = new WebProxy(proxy), }; this.client = new HttpClient(handler: httpClientHandler, disposeHandler: true); } this.client.Timeout = TimeSpan.FromSeconds(60); this.createTime = DateTime.Now; } } private readonly HttpClient http; private readonly string baseUrl; private readonly string proxy; private readonly int timeout; public HttpConnection(string baseUrl, int timeout, string proxy, HttpClient http) { this.proxy = string.IsNullOrEmpty(proxy) ? "" : proxy; this.timeout = timeout; this.baseUrl = baseUrl; if (http != null) { this.http = http; } else { this.http = HttpClientHolder.GetClient(this.proxy); } } private static string AppendQuery(StringBuilder builder, Dictionary<string, string> param) { foreach (KeyValuePair<string, string> kvp in param) { builder.Append($"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}&"); } return builder.ToString().TrimEnd('&'); } public async Task<HttpResponseMessage> GetRequestAsync(string url, Dictionary<string, string> param) { StringBuilder urlBuilder = new StringBuilder($"{baseUrl.TrimEnd('/')}{url}?"); string fullurl = AppendQuery(urlBuilder, param); string payload = ""; Dictionary<string, string> headers = new Dictionary<string, string>(); return await this.Send(HttpMethod.Get, fullurl, payload, headers); } public async Task<HttpResponseMessage> GetRequestAsync(string path, string queryString, Dictionary<string, string> headers) { string fullurl = $"{this.baseUrl.TrimEnd('/')}{path}?{queryString}"; string payload = ""; return await this.Send(HttpMethod.Get, fullurl, payload, headers); } public async Task<HttpResponseMessage> PostRequestAsync(string path, string payload, Dictionary<string, string> headers) { string fullurl = $"{baseUrl.TrimEnd('/')}{path}"; return await this.Send(HttpMethod.Post, fullurl, payload, headers); } public async Task<HttpResponseMessage> PostRequestAsync(string url, Dictionary<string, string> param) { string fullurl = $"{this.baseUrl.TrimEnd('/')}{url}?"; StringBuilder payloadBuilder = new StringBuilder(); string payload = AppendQuery(payloadBuilder, param); Dictionary<string, string> headers = new Dictionary<string, string>(); headers["Content-Type"] = "application/x-www-form-urlencoded"; return await this.Send(HttpMethod.Post, fullurl, payload, headers); } private async Task<HttpResponseMessage> Send(HttpMethod method, string url, string payload, Dictionary<string, string> headers) { using (var cts = new System.Threading.CancellationTokenSource(timeout * 1000)) { using (var msg = new HttpRequestMessage(method, url)) { foreach (KeyValuePair<string, string> kvp in headers) { if (kvp.Key.Equals("Content-Type")) { msg.Content = new StringContent(payload, Encoding.UTF8, kvp.Value); } else if (kvp.Key.Equals("Host")) { msg.Headers.Host = kvp.Value; } else if (kvp.Key.Equals("Authorization")) { msg.Headers.Authorization = new AuthenticationHeaderValue("TC3-HMAC-SHA256", kvp.Value.Substring("TC3-HMAC-SHA256".Length)); } else { msg.Headers.Add(kvp.Key, kvp.Value); } } return await http.SendAsync(msg, cts.Token); } } } } }
/* | |
* Copyright (c) 2018 THL A29 Limited, a Tencent company. All Rights Reserved. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Net; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace TencentCloud.Common.Http | |
{ | |
public class HttpConnection | |
{ | |
private class HttpClientHolder | |
{ | |
private static readonly ConcurrentDictionary<string, HttpClientHolder> httpclients = new ConcurrentDictionary<string, HttpClientHolder>(); | |
public static HttpClient GetClient(string proxy) | |
{ | |
string key = string.IsNullOrEmpty(proxy) ? "" : proxy; | |
HttpClientHolder result = httpclients.GetOrAdd(key, (k) => | |
{ | |
return new HttpClientHolder(k); | |
}); | |
TimeSpan timeSpan = DateTime.Now - result.createTime; | |
// 每5分钟创建一个新的连接,放弃旧连接,避免DNS刷新问题。 | |
while (timeSpan.TotalSeconds > 300) | |
{ | |
ICollection<KeyValuePair<string, HttpClientHolder>> kv = httpclients; | |
kv.Remove(new KeyValuePair<string, HttpClientHolder>(key, result)); | |
result = httpclients.GetOrAdd(key, (k) => | |
{ | |
return new HttpClientHolder(k); | |
}); | |
timeSpan = DateTime.Now - result.createTime; | |
} | |
return result.client; | |
} | |
public readonly HttpClient client; | |
public readonly DateTime createTime; | |
public HttpClientHolder(string proxy) | |
{ | |
string p = string.IsNullOrEmpty(proxy) ? "" : proxy; | |
if (p == "") | |
{ | |
this.client = new HttpClient(); | |
} | |
else | |
{ | |
var httpClientHandler = new HttpClientHandler | |
{ | |
Proxy = new WebProxy(proxy), | |
}; | |
this.client = new HttpClient(handler: httpClientHandler, disposeHandler: true); | |
} | |
this.client.Timeout = TimeSpan.FromSeconds(60); | |
this.createTime = DateTime.Now; | |
} | |
} | |
private readonly HttpClient http; | |
private readonly string baseUrl; | |
private readonly string proxy; | |
private readonly int timeout; | |
public HttpConnection(string baseUrl, int timeout, string proxy, HttpClient http) | |
{ | |
this.proxy = string.IsNullOrEmpty(proxy) ? "" : proxy; | |
this.timeout = timeout; | |
this.baseUrl = baseUrl; | |
if (http != null) | |
{ | |
this.http = http; | |
} | |
else | |
{ | |
this.http = HttpClientHolder.GetClient(this.proxy); | |
} | |
} | |
private static string AppendQuery(StringBuilder builder, Dictionary<string, string> param) | |
{ | |
foreach (KeyValuePair<string, string> kvp in param) | |
{ | |
builder.Append($"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}&"); | |
} | |
return builder.ToString().TrimEnd('&'); | |
} | |
public async Task<HttpResponseMessage> GetRequestAsync(string url, Dictionary<string, string> param) | |
{ | |
StringBuilder urlBuilder = new StringBuilder($"{baseUrl.TrimEnd('/')}{url}?"); | |
string fullurl = AppendQuery(urlBuilder, param); | |
string payload = ""; | |
Dictionary<string, string> headers = new Dictionary<string, string>(); | |
return await this.Send(HttpMethod.Get, fullurl, payload, headers); | |
} | |
public async Task<HttpResponseMessage> GetRequestAsync(string path, string queryString, Dictionary<string, string> headers) | |
{ | |
string fullurl = $"{this.baseUrl.TrimEnd('/')}{path}?{queryString}"; | |
string payload = ""; | |
return await this.Send(HttpMethod.Get, fullurl, payload, headers); | |
} | |
public async Task<HttpResponseMessage> PostRequestAsync(string path, string payload, Dictionary<string, string> headers) | |
{ | |
string fullurl = $"{baseUrl.TrimEnd('/')}{path}"; | |
return await this.Send(HttpMethod.Post, fullurl, payload, headers); | |
} | |
public async Task<HttpResponseMessage> PostRequestAsync(string url, Dictionary<string, string> param) | |
{ | |
string fullurl = $"{this.baseUrl.TrimEnd('/')}{url}?"; | |
StringBuilder payloadBuilder = new StringBuilder(); | |
string payload = AppendQuery(payloadBuilder, param); | |
Dictionary<string, string> headers = new Dictionary<string, string>(); | |
headers["Content-Type"] = "application/x-www-form-urlencoded"; | |
return await this.Send(HttpMethod.Post, fullurl, payload, headers); | |
} | |
private async Task<HttpResponseMessage> Send(HttpMethod method, string url, string payload, Dictionary<string, string> headers) | |
{ | |
using (var cts = new System.Threading.CancellationTokenSource(timeout * 1000)) | |
{ | |
using (var msg = new HttpRequestMessage(method, url)) | |
{ | |
foreach (KeyValuePair<string, string> kvp in headers) | |
{ | |
if (kvp.Key.Equals("Content-Type")) | |
{ | |
msg.Content = new StringContent(payload, Encoding.UTF8, kvp.Value); | |
} | |
else if (kvp.Key.Equals("Host")) | |
{ | |
msg.Headers.Host = kvp.Value; | |
} | |
else if (kvp.Key.Equals("Authorization")) | |
{ | |
msg.Headers.Authorization = new AuthenticationHeaderValue("TC3-HMAC-SHA256", kvp.Value.Substring("TC3-HMAC-SHA256".Length)); | |
} | |
else | |
{ | |
msg.Headers.Add(kvp.Key, kvp.Value); | |
} | |
} | |
return await http.SendAsync(msg, cts.Token); | |
} | |
} | |
} | |
} | |
} |