.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. | |
| * | |
| * 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 | |
| * 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); | |
| } | |
| } | |
| } | |
| } | |
| } |