c# - consumir - HttpClient vs HttpWebRequest para un mejor rendimiento y seguridad y menos conexiones
webclient vs httpclient (3)
Hay un problema en su implementación ''A''. El tiempo de vida de la instancia devuelta desde
GetWebRequestHandler()
es de corta duración (¿tal vez solo por el ejemplo?). Si esto se hizo a propósito, niega el paso defalse
para el segundo parámetro del constructorHttpClient
. El valor defalse
le dice al HttpClient que no deseche elHttpMessageHandler
subyacente (lo que ayuda a escalar ya que no cierra el puerto para la solicitud). Esto es, por supuesto, asumiendo que la vida útil deHttpMessageHandler
es lo suficientemente larga como para que pueda aprovechar el beneficio de no abrir / cerrar puertos (lo que es un gran impacto en la escalabilidad de su servidor). Por lo tanto, tengo una recomendación a continuación de la opción ''D''.También hay una opción ''D'' que no
Httpclient
la lista anterior: para que la instancia deHttpclient
static
y se reutilice en todas las llamadas a la API. Esto es mucho más eficiente desde la asignación de memoria y la perspectiva del GC, así como la apertura de puertos en el cliente. No tiene la sobrecarga de asignar memoria para crear instancias deHttpClient
(y todos sus objetos subyacentes) y, por lo tanto, evitar la limpieza a través de GC para ellos.
Consulte mi respuesta provista sobre una pregunta similar: ¿Cuál es la sobrecarga de crear un nuevo HttpClient por llamada en un cliente WebAPI?
He hecho algunas pruebas simples para comparar y alguna información que descubrí fue
Un solo HttpClient se puede compartir mediante una solicitud múltiple, si se comparte y las solicitudes se envían al mismo destino, la solicitud múltiple puede reutilizar las conexiones que WebRequest necesita para recrear la conexión para cada solicitud.
También busqué documentación sobre otras formas de usar el ejemplo de HttpClient
El siguiente artículo resume la conexión compartida autenticada con NTLM de alta velocidad
HttpWebRequest.UnsafeAuthenticatedConnectionSharing
A continuación se muestran las posibles implementaciones que probé.
UNA)
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))
{
}
SEGUNDO)
using (HttpClient client = new HttpClient)
{
}
DO)
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("some uri string")
Agradecería cualquier ayuda para hacerme entender qué enfoque debería tomar para lograr el máximo rendimiento, minimizar las conexiones y asegurarme de que la seguridad no se vea afectada.
Este es mi ApiClient que crea el HttpClient solo una vez. Registre este objeto como singleton en su biblioteca de inyección de dependencias. Es seguro reutilizarlo porque es apátrida. NO vuelva a crear HTTPClient para cada solicitud. Reutilizar Httpclient tanto como sea posible
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 + "/";
}
}
El uso;
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;
}
Si utiliza cualquiera de ellos con async, debería ser bueno para el punto de vista del rendimiento, ya que no bloqueará los recursos que esperan la respuesta y obtendrá un buen rendimiento.
Se prefiere HttpClient sobre HttpWebRequest debido a los métodos asíncronos disponibles fuera de la caja y no debería preocuparse por escribir métodos de inicio / finalización.
Básicamente, cuando utiliza una llamada asíncrona (utilizando cualquiera de las clases), no bloqueará los recursos que esperan la respuesta y cualquier otra solicitud utilizará los recursos para realizar más llamadas.
Otra cosa a tener en cuenta es que no debe utilizar HttpClient en el bloque ''usar'' para permitir la reutilización de los mismos recursos una y otra vez para otras solicitudes web.
Vea el siguiente hilo para más información.