我有一个 .Net Core 3.1 应用程序。在应用程序中,我使用 IHttpClientFactory 创建 HttpClient。当我使用 SendAsync 进行调用时,第一个请求需要 2 秒多的时间,而后续请求需要不到 100 毫秒。对于生产应用程序来说,这是不可接受的性能。
我还注意到,如果我一段时间没有提出任何请求,就会发生这种情况。我遇到了 PooledConnectionIdleTimeout 属性,该属性默认为 2 分钟,我可以延长该时间,但这仅适用于已存在的池连接,不适用于需要创建新连接的情况。
我在 Startup.cs 中配置 HttpClient,如下所示:
services.AddHttpClient("Default")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new SocketsHttpHandler
{
UseCookies = false,
AllowAutoRedirect = false,
MaxConnectionsPerServer = int.MaxValue,
UseProxy = false
};
});
Run Code Online (Sandbox Code Playgroud)
我有一个 Singleton 包装类,其中包含一个调用 SendAsync 的方法:
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken = default)
{
var client = m_HttpClientFactory.CreateClient("Default");
return await client.SendAsync(request, completionOption, cancellationToken).ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
我缺少某种配置吗?或者我可以做一些预热,这样第一个请求就不会花很长时间?
更新:已经过去几个月了,我不记得所有细节,但考虑到最近的评论,我决定发布我的发现和解决方法。
我发现当使用 IP 地址作为主机名而不是服务器名称时,不会出现此问题。就我而言,我几乎总是调用 localhost,因此我会比较我应该访问的主机名,如果它与本地系统的主机名匹配,我会将主机更改为 127.0.0.1。 …
有人知道为什么下面的策略在 3 次而不是 10 次后停止重试吗?
IAsyncPolicy<HttpResponseMessage> httpWaitAndRetryPolicy =
Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.OrHandle<Exception>(r => true)
.WaitAndRetryAsync(10, retryAttempt => TimeSpan.FromSeconds(2));
Run Code Online (Sandbox Code Playgroud)
我将重试尝试设置为 10 并测试 http post 调用,但 BadRequest 失败。但只重试了3次就停止了,直到超时并抛出异常
IAsyncPolicy<HttpResponseMessage> httpWaitAndRetryPolicy =
Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.OrHandle<Exception>(r => true)
.WaitAndRetryAsync(10, retryAttempt => TimeSpan.FromSeconds(2));
Run Code Online (Sandbox Code Playgroud)
var serviceProvider = serviceConnection.AddHttpClient(connection.Name, c =>
{
c.BaseAddress = new Uri(connection.BaseUrl);
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{connection.UserName}:{connection.Password}")));
c.Timeout = connection.Timeout; // Timeout is TimeSpan.FromSeconds(120)
})
.AddPolicyHandler(httpWaitAndRetryPolicy)
.Services.BuildServiceProvider();
HttpClientFactories.Add(connection.Name, serviceProvider.GetService<IHttpClientFactory>());
Run Code Online (Sandbox Code Playgroud)
确认了问题的根本原因:我不知道是什么原因导致了该症状,但看起来请求连接不会被释放,除非显式调用Dispose HttpResponseMessage OnRetry. 当前的解决方案是设置OnRetry并WaitAndRetryAsync配置响应。一切正常,无需更改ServicePointManager.DefaultConnectionLimit
我刚刚读到Polly 库,当从桌面代理到服务器通信时,我需要处理 3 次重试。
目前我喜欢指数退避。
但是,我很难理解如何在我的代码中实现这一点。这是我到目前为止所做的:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
client.DefaultRequestHeaders.Add("TenantId", tenantId);
#if DEBUG
var url = "http://localhost:5001/api/v1/RetrievePaymentDetails?orderpaymentid={orderPaymentId";
#else
//TODO: add URL once the application is in production!
var url = "intent2.api";
#endif
Policy
.Handle<HttpRequestException>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(15),
TimeSpan.FromSeconds(30)
});
using (HttpResponseMessage response = await client.GetAsync($"{url}"))
{
using (HttpContent content = response.Content)
{
HttpContentHeaders headers = content.Headers;
var result = JsonConvert.DeserializeObject<PaymentDetailsDto>(response.Content.ReadAsStringAsync().Result);
Currency currencyFromDb = (Currency)Enum.Parse(typeof(Currency), result.Currency);
var result2 = …Run Code Online (Sandbox Code Playgroud) 在 C# 中,使用 HttpClient 时,如何连接到使用自签名证书(用于测试)或不属于计算机信任存储的自定义 CA 的 https 服务器?请注意,我不需要客户端证书,只需要 HttpClient 来验证未由计算机上受信任的根 CA 之一签名的服务器证书。我知道我可以将自签名证书或 CA 添加到计算机上的本地信任存储中,但假设我想避免这样做。我需要的基本上是为正在运行的应用程序提供额外的根 CA。
据我所知,在其他语言中有很简单的方法可以做到这一点:
在java中,使用“javax.net.ssl.trustStore”系统属性。
在 Node.js 中,使用 NODE_EXTRA_CA_CERT 环境变量。
但到目前为止,我在 .Net 上找不到类似的东西。有没有像上面这样简单的方法呢?
我已将应用程序从 NetCore 3.1 和 Ubuntu 18.04 迁移到 Net5 和 Ubuntu 20.04。
当 HttpWebRequest 对象调用 GetResponse 方法时,应用程序开始失败。
我发现了来自 Microsoft 的通知,警告有关行为的重大变化,请参阅
https://medium.com/siberians-pro/how-to-use-tlsv1-on-ubuntu-20-04-133c2898ad7中有一篇非常有趣的文章(Linux 通用,与 Net5 无关)
但两者都不适合我。
到目前为止我已经尝试过的:
openssl_conf = openssl_init
[openssl_init]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
MinProtocol = TLSv1
CipherString = DEFAULT@SECLEVEL=1
Run Code Online (Sandbox Code Playgroud)
OPENSSL_CONF我已经导出了指向该文件的变量export OPENSSL_CONF=/var/www/spider/openssl.cnf
Run Code Online (Sandbox Code Playgroud)
dotnet Spider.dll
Run Code Online (Sandbox Code Playgroud)
fail: Spider.Program[0]
[19/12/2020 08:50:52.724]: Error getting response from https://www.boe.es/diario_boe/xml.php?id=BOE-S-20201216
System.Net.WebException: The SSL connection could not be established, see inner exception.
---> System.Net.Http.HttpRequestException: The SSL …Run Code Online (Sandbox Code Playgroud) 我正在尝试让 HttpClient 从网站中提取网站图标,并且在 99% 的情况下,此代码可以按预期工作:
var httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromSeconds(10);
var response = await httpClient.GetAsync("https://www.tesco.com/favicon.ico");
response.EnsureSuccessStatusCode();
Run Code Online (Sandbox Code Playgroud)
我在几个网站上发现我的 GetAsync 方法只是超时,我相信这与其重定向有关。如果在控制台应用程序中运行上述代码,10 秒后会在 TaskCanceledException 和 IOException 中引发以下异常:
SocketException:由于线程退出或应用程序请求,I/O 操作已中止。
这个请求在 Postman 上完全正常,我尝试过 https 和 http 但没有成功。当访问该网站并使用 Chrome 的开发工具时,看起来如果您使用 http 而不是 https,它会返回 307 并将您重定向到 https 网站,然后返回 304。我不明白为什么 HttpClient 只是计时而不是给出有用的回应。
有什么办法让它发挥作用吗?我错过了一些简单的事情吗?
更新 - 2021-01-29
我在多个不同的 .NET 版本上进行了尝试,发现这可能是 HttpClient 的一个错误,因为此代码适用于 .NET Core 2.0,但不适用于 .NET Core 2.1。
已测试版本
我有这样的工作流程:
两种 API 方法都具有相同类型的结果。我想通过 Polly 政策来实现这一点。
这是我的示例代码:
var retryPolicy = Policy
.Handle<HttpRequestException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.RetryAsync(1, async (exception, retryCount) =>
await CallAnotherAPI());
var fallbackPolicy = Policy<HttpResponseMessage>
.Handle<Exception>()
.FallbackAsync((r, c, ct) => throw r.Exception,
async (r, c) =>
{
Log(r.Message);
});
var result = await fallbackPolicy
.WrapAsync(retryPolicy)
.ExecuteAsync(async () =>
{
await CallAPI();
});
Run Code Online (Sandbox Code Playgroud)
但它不起作用并且一直执行fallbackPolicy。如何编写如果retryPolicy为true,则不会执行fallbackPolicy的代码?
我在 Startup.ConfigureServices 方法中在 HttpClient 上创建了重试策略。另请注意,默认情况下,asp.net core 2.1 为 HttpClient 进行的每个调用记录 4 行 [信息] 行,这些行显示在我的问题末尾的日志中。
\nservices.AddHttpClient("ResilientClient")\n .AddPolicyHandler(\n Policy.WrapAsync(\n PollyRetryPolicies.TransientErrorRetryPolicy(),\n Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(60))));\nRun Code Online (Sandbox Code Playgroud)\n该策略定义如下。请注意,我将重试尝试写入日志,因此我会知道是否调用了重试策略。
\npublic static IAsyncPolicy < HttpResponseMessage > TransientErrorRetryPolicy() {\n return HttpPolicyExtensions\n .HandleTransientHttpError()\n .Or < TimeoutRejectedException > ()\n .WaitAndRetryAsync(sleepDurations: ExponentialBackoffPolicy.DecorrelatedJitter(3, SEED_DELAY, MAX_DELAY),\n onRetry: (message, timespan, attempt, context) => {\n context.GetLogger() ? .LogInformation($ "Retrying request to {message?.Result?.RequestMessage?.RequestUri} in {timespan.TotalSeconds} seconds. Retry attempt {attempt}.");\n });\n}\nRun Code Online (Sandbox Code Playgroud)\nHandleTransientHttpError() 是一个 Polly 扩展,它在注释中指出:
\n\n\n配置要处理的条件是:\n\xe2\x80\xa2 网络故障(如 System.Net.Http.HttpRequestException)
\n
我的httpclient用法是这样的:
\nusing (HttpResponseMessage …Run Code Online (Sandbox Code Playgroud) 在.NET 6 中:
接收该HttpClient实例的类型被注册为“Scoped”的类型化实例的生命周期是多少?IHttpClientFactory
当我们像下面的摘录那样注册它时,它不应该是一种“定时单例”(无论使用它的类的生命周期如何)吗?或者是HttpClient Transient - 并且 .NET 不缓存其任何配置,并且仅池化处理程序?
services.AddHttpClient<IServiceInterface, ServiceImpl>(client =>
{
client.BaseAddress = "<some absolute URL here>";
}
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
services.AddScoped<IServiceInterface, ServiceImpl>();
Run Code Online (Sandbox Code Playgroud)
我正在开发的应用程序访问不同地址的多个外部 API。我将每个服务访问逻辑封装到具有各自接口的服务类中,以便可以在运行时注入它们。按照 Microsoft 的规定,我使用的是 Typed HttpClients,并且我编写了一个辅助方法来配置它们Startup.cs:
public static IServiceCollection ConfigureHttpClientForService<TInterface, TImpl>
(this IServiceCollection services, Func<IServiceProvider, Uri> func)
where TInterface : class
where TImpl : class, TInterface
{
services.AddHttpClient<TInterface, TImpl>((provider, client) =>
{
var uri = func(provider);
client.BaseAddress = uri;
})
// Polly Rules here and other …Run Code Online (Sandbox Code Playgroud) .net c# dependency-injection dotnet-httpclient httpclientfactory
是否可以在.Net/Web Api中设置HttpClient的请求ACCEPT标头以包含"application/json; odata = verbose"?
我知道如何设置请求媒体类型
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Run Code Online (Sandbox Code Playgroud)
但是如何设置odata = verbose部分呢?我似乎无法在网上找到任何解决方案.
我是否必须使用HttpWebRequest?基本上我需要调用sharepoint 2013 rest api,并且需要odata = verbose部分.
c# ×9
polly ×4
retry-logic ×4
.net-core ×3
.net-5 ×2
.net ×1
asp.net ×1
asp.net-core ×1
ca ×1
fallback ×1
httpresponse ×1
rest ×1
ssl ×1
tls1.2 ×1
ubuntu-20.04 ×1