HttpClient vs HttpWebRequest 以获得更好的性能、安全性和更少的连接
Posted
技术标签:
【中文标题】HttpClient vs HttpWebRequest 以获得更好的性能、安全性和更少的连接【英文标题】:HttpClient vs HttpWebRequest for better performance, security and less connections 【发布时间】:2015-01-06 07:07:37 【问题描述】:我发现一个 HttpClient 可以被多个请求共享。如果共享,并且请求到达相同的目的地,则多个请求可以重用连接。 WebRequest 需要为每个请求重新创建连接。
我还在示例中查找了有关使用 HttpClient 的其他方法的一些文档。
以下文章总结了高速NTLM认证的连接共享:HttpWebRequest.UnsafeAuthenticatedConnectionSharing
我尝试过的可能实现如下所示
一)
private WebRequestHandler GetWebRequestHandler()
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(ResourceUriCanBeAnyUri, "NTLM", CredentialCache.DefaultNetworkCredentials);
WebRequestHandler handler = new WebRequestHandler
UnsafeAuthenticatedConnectionSharing = true,
Credentials = credentialCache
;
return handler;
using (HttpClient client = new HttpClient(GetWebRequestHandler(), false))
B)
using (HttpClient client = new HttpClient)
C)
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("some uri string")
如果能帮助我了解我应该采用哪种方法以实现最大性能、最小化连接并确保不影响安全性,我将不胜感激。
【问题讨论】:
HttpClient 是镇上新来的酷孩子,据说它是最好的,支持异步/任务,并且比其他的更便携(还有 WebClient)。但是它需要 .NET 4.5+。话虽如此,我认为如果使用得当,它们之间的原始性能不会有太大差异。 我你用HttpClient看看这个帖子YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE 肯定会使用 HttpClient,除了它处理连接池、开箱即用的 async/await 之外,它还通过它们提供了更大的灵活性 Handlers 并且也更容易使用 HttpClient 编写单元测试。 HttpClient 旨在被实例化一次并在应用程序的整个生命周期中重复使用。特别是在服务器应用程序中,为每个请求创建一个新的 HttpClient 实例将耗尽重负载下可用的套接字数量。这将导致 SocketException 错误。示例:asp.net/web-api/overview/advanced/… 先看看这个:docs.microsoft.com/en-us/archive/blogs/timomta/… DefaultConnectionLimit 参数可能会导致客户端瓶颈 【参考方案1】:如果您将它们中的任何一个与异步一起使用,从性能角度来看应该是好的,因为它不会阻塞等待响应的资源,并且您将获得良好的吞吐量。
HttpClient 优于 HttpWebRequest,因为异步方法可用开箱即用,您不必担心编写开始/结束方法。
基本上,当您使用异步调用(使用任一类)时,它不会阻塞等待响应的资源,任何其他请求都会利用这些资源进行进一步的调用。
要记住的另一件事是,您不应该在“使用”块中使用 HttpClient,以允许将相同的资源一次又一次地重用于其他 Web 请求。
更多信息请看下面的帖子
Do HttpClient and HttpClientHandler have to be disposed?
【讨论】:
是的,刚刚学会了自己处理 HttpClient。似乎是现在的大讨论。长话短说,不要丢弃它!HttpWeRequest
也支持异步,所以这个答案已经过时了【参考方案2】:
这是我的 ApiClient,它只创建一次 HttpClient。将此对象注册为单例到您的依赖注入库。重用是安全的,因为它是无状态的。不要为每个请求重新创建 HTTPClient。尽可能复用Httpclient
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
//You need to install package Newtonsoft.Json > https://www.nuget.org/packages/Newtonsoft.Json/
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class MyApiClient : IDisposable
private readonly TimeSpan _timeout;
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
private readonly string _baseUrl;
private const string ClientUserAgent = "my-api-client-v1";
private const string MediaTypeJson = "application/json";
public MyApiClient(string baseUrl, TimeSpan? timeout = null)
_baseUrl = NormalizeBaseUrl(baseUrl);
_timeout = timeout ?? TimeSpan.FromSeconds(90);
public async Task<string> PostAsync(string url, object input)
EnsureHttpClientCreated();
using (var requestContent = new StringContent(ConvertToJsonString(input), Encoding.UTF8, MediaTypeJson))
using (var response = await _httpClient.PostAsync(url, requestContent))
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
public async Task<TResult> PostAsync<TResult>(string url, object input) where TResult : class, new()
var strResponse = await PostAsync(url, input);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
ContractResolver = new CamelCasePropertyNamesContractResolver()
);
public async Task<TResult> GetAsync<TResult>(string url) where TResult : class, new()
var strResponse = await GetAsync(url);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
ContractResolver = new CamelCasePropertyNamesContractResolver()
);
public async Task<string> GetAsync(string url)
EnsureHttpClientCreated();
using (var response = await _httpClient.GetAsync(url))
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
public async Task<string> PutAsync(string url, object input)
return await PutAsync(url, new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, MediaTypeJson));
public async Task<string> PutAsync(string url, HttpContent content)
EnsureHttpClientCreated();
using (var response = await _httpClient.PutAsync(url, content))
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
public async Task<string> DeleteAsync(string url)
EnsureHttpClientCreated();
using (var response = await _httpClient.DeleteAsync(url))
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
public void Dispose()
_httpClientHandler?.Dispose();
_httpClient?.Dispose();
private void CreateHttpClient()
_httpClientHandler = new HttpClientHandler
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
;
_httpClient = new HttpClient(_httpClientHandler, false)
Timeout = _timeout
;
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
if (!string.IsNullOrWhiteSpace(_baseUrl))
_httpClient.BaseAddress = new Uri(_baseUrl);
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
private void EnsureHttpClientCreated()
if (_httpClient == null)
CreateHttpClient();
private static string ConvertToJsonString(object obj)
if (obj == null)
return string.Empty;
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
ContractResolver = new CamelCasePropertyNamesContractResolver()
);
private static string NormalizeBaseUrl(string url)
return url.EndsWith("/") ? url : url + "/";
用法;
using ( var client = new MyApiClient("http://localhost:8080"))
var response = client.GetAsync("api/users/findByUsername?username=alper").Result;
var userResponse = client.GetAsync<MyUser>("api/users/findByUsername?username=alper").Result;
【讨论】:
应该等待而不是.Result
那些方法。【参考方案3】:
您的实现“A”中存在问题。从GetWebRequestHandler()
返回的实例的生命周期是短暂的(也许只是为了示例?)。如果这是故意这样做的,它将否定 false
为 HttpClient
构造函数的第二个参数传递。 false
的值告诉 HttpClient 不要处理底层的HttpMessageHandler
(这有助于扩展,因为它不会关闭请求的端口)。当然,这是假设HttpMessageHandler
的生命周期足够长,您可以利用不打开/关闭端口的好处(这对您的服务器的可伸缩性有很大影响)。因此,我有以下选项“D”的建议。
还有一个您没有在上面列出的选项“D” - 使 Httpclient
实例 static
并在所有 api 调用中重复使用。从内存分配和 GC 的角度来看,这更有效 - 以及在客户端上打开端口。您无需为HttpClient
实例(及其所有底层对象)分配内存和创建开销,因此也无需通过 GC 对它们进行清理。
请参考我提供的关于类似问题的回答 - What is the overhead of creating a new HttpClient per call in a WebAPI client?
【讨论】:
以上是关于HttpClient vs HttpWebRequest 以获得更好的性能、安全性和更少的连接的主要内容,如果未能解决你的问题,请参考以下文章
HttpClient vs HttpWebRequest 以获得更好的性能、安全性和更少的连接
HttpClient不适用于使用Xamarin在VS中构建的IO应用程序
.NET Core 2.2 HttpClient/WebClient vs Curl - .NET 库对于某些服务器来说非常慢