标签: dotnet-httpclient

.Net Core 3.1 IHttpClientFactory/HttpClient 首次请求时速度缓慢

我有一个 .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。 …

c# dotnet-httpclient .net-core .net-core-3.1

5
推荐指数
1
解决办法
2276
查看次数

HttpClient Polly WaitAndRetry 策略

有人知道为什么下面的策略在 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. 当前的解决方案是设置OnRetryWaitAndRetryAsync配置响应。一切正常,无需更改ServicePointManager.DefaultConnectionLimit

c# rest dotnet-httpclient polly retry-logic

5
推荐指数
1
解决办法
9057
查看次数

在 Polly 库中使用 httpclient 的指数退避

我刚刚读到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# httpresponse dotnet-httpclient polly retry-logic

5
推荐指数
2
解决办法
6780
查看次数

将自定义根 CA 与 HttpClient 结合使用

在 C# 中,使用 HttpClient 时,如何连接到使用自签名证书(用于测试)或不属于计算机信任存储的自定义 CA 的 https 服务器?请注意,我不需要客户端证书,只需要 HttpClient 来验证未由计算机上受信任的根 CA 之一签名的服务器证书。我知道我可以将自签名证书或 CA 添加到计算机上的本地信任存储中,但假设我想避免这样做。我需要的基本上是为正在运行的应用程序提供额外的根 CA。

据我所知,在其他语言中有很简单的方法可以做到这一点:

  1. 在java中,使用“javax.net.ssl.trustStore”系统属性。

  2. 在 Node.js 中,使用 NODE_EXTRA_CA_CERT 环境变量。

但到目前为止,我在 .Net 上找不到类似的东西。有没有像上面这样简单的方法呢?

c# ssl ca dotnet-httpclient .net-core

5
推荐指数
1
解决办法
2704
查看次数

无法在 Ubuntu 20.04 上使用 Net5(OpenSSL 连接问题)

我已将应用程序从 NetCore 3.1 和 Ubuntu 18.04 迁移到 Net5 和 Ubuntu 20.04。

当 HttpWebRequest 对象调用 GetResponse 方法时,应用程序开始失败。

我发现了来自 Microsoft 的通知,警告有关行为的重大变化,请参阅

https://learn.microsoft.com/en-US/dotnet/core/compatibility/cryptography/5.0/default-cipher-suites-for-tls-on-linux

https://medium.com/siberians-pro/how-to-use-tlsv1-on-ubuntu-20-04-133c2898ad7中有一篇非常有趣的文章(Linux 通用,与 Net5 无关)

但两者都不适合我。

到目前为止我已经尝试过的:

  1. 我创建了一个特定的本地 openssl.cnf 文件,其中包含以下内容
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)
  1. OPENSSL_CONF我已经导出了指向该文件的变量
export OPENSSL_CONF=/var/www/spider/openssl.cnf 
Run Code Online (Sandbox Code Playgroud)
  1. 我正常启动我的代码
dotnet Spider.dll
Run Code Online (Sandbox Code Playgroud)
  1. 我得到了例外
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)

dotnet-httpclient tls1.2 asp.net-core .net-5 ubuntu-20.04

5
推荐指数
1
解决办法
5303
查看次数

C# HttpClient 不处理 304/307 重定向

我正在尝试让 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。

已测试版本

  • .NET Framework 4.8:有效
  • .NET Core 1.0:有效
  • .NET Core 1.1:有效
  • .NET Core 2.0:有效
  • .NET Core 2.1:不起作用
  • .NET …

c# http-redirect dotnet-httpclient .net-core .net-5

5
推荐指数
1
解决办法
1595
查看次数

C# 中的 Polly 模式适用于工作流程?

我有这样的工作流程:

  • 调用API方法
    • 如果抛出任何异常但超时,程序会记录该异常并抛出它。
  • 如果抛出超时,程序必须调用另一个 API 方法。
    • 调用另一个 API 方法后,如果一切正常,程序将返回结果。
    • 否则程序会抛出异常并记录它。

两种 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的代码?

c# fallback dotnet-httpclient polly retry-logic

5
推荐指数
1
解决办法
973
查看次数

Polly HandleTransientHttpError 未捕获 HttpRequestException

我在 Startup.ConfigureServices 方法中在 HttpClient 上创建了重试策略。另请注意,默认情况下,asp.net core 2.1 为 HttpClient 进行的每个调用记录 4 行 [信息] 行,这些行显示在我的问题末尾的日志中。

\n
services.AddHttpClient("ResilientClient")\n            .AddPolicyHandler(\n                Policy.WrapAsync(\n                    PollyRetryPolicies.TransientErrorRetryPolicy(),\n                    Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(60))));\n
Run Code Online (Sandbox Code Playgroud)\n

该策略定义如下。请注意,我将重试尝试写入日志,因此我会知道是否调用了重试策略。

\n
public 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}\n
Run Code Online (Sandbox Code Playgroud)\n

HandleTransientHttpError() 是一个 Polly 扩展,它在注释中指出:

\n
\n

配置要处理的条件是:\n\xe2\x80\xa2 网络故障(如 System.Net.Http.HttpRequestException)

\n
\n

我的httpclient用法是这样的:

\n
using (HttpResponseMessage …
Run Code Online (Sandbox Code Playgroud)

c# dotnet-httpclient polly retry-logic asp.net-core-2.1

5
推荐指数
1
解决办法
5878
查看次数

来自 IHttpClientFactory 的类型化 HttpClient 实例的生命周期是多少,其中将接收该实例的类型注册为“AddScoped”?

长话短说

在.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

5
推荐指数
1
解决办法
1769
查看次数

HttpClient请求标头自定义

是否可以在.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# asp.net asp.net-mvc-4 asp.net-web-api dotnet-httpclient

4
推荐指数
1
解决办法
4275
查看次数