【问题标题】:.NET Core 3.1 HttpClient throws exception intermittently: SocketException: The I/O operation has been aborted because of either a thread exit or an.NET Core 3.1 HttpClient 间歇性抛出异常:SocketException: I/O 操作已被中止,因为线程退出或
【发布时间】:2021-01-02 00:57:07
【问题描述】:

我偶尔会遇到以下异常:

IOException:无法从传输连接中读取数据: 由于线程退出或 申请要求..

SocketException: I/O 操作已被中止,因为 线程退出或应用程序请求。

系统正在查询外部资源,有时会发生异常,但似乎没有任何异常。我试图为HttpClient 设置更长的超时时间,但没有帮助。在异常发生之前,它可能是 5000-50000 次搜索,但我仍然想减轻它。如果我在出现异常后直接重试相同的搜索,它会起作用,因此即使我无法访问该应用程序日志,接收方似乎也没有问题。在.NET Core 3.1 上运行。

MyService.cs

public class MyService
{

    private readonly HttpClient _httpClient;

    public MyService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://example.com/");
        client.Timeout = TimeSpan.FromMinutes(5);
        _httpClient = client;
    }
    
    private async Task<List<string>> GetValuesFromSearch(string search)
    {
        //Exception is thrown here
        var response = await _httpClient.GetAsync("search/" + search);

        using var responseStream = await response.Content.ReadAsStreamAsync();
        
        response.EnsureSuccessStatusCode();

        var searchResultList = await JsonSerializer.DeserializeAsync
            <List<string>>(responseStream);

        return searchResultList;
    }
}

这样称呼:

var myService = new MyService(new HttpClient());

foreach (var search in listToIterate)
{
    //Can be called up to 200 000 times
    var result = await myService.GetValuesFromSearch(search);
}

【问题讨论】:

  • @user1672994 谢谢,但由于 C# 8 using 应该可以像这样使用。 docs.microsoft.com/en-us/dotnet/csharp/language-reference/… 我还在await response.Content.ReadAsStreamAsync(); 之后检查EnsureSuccessStatusCode,以便我可以阅读任何错误的正文。鉴于异常发生在await _httpClient.GetAsync("search/" + search);,我认为它不相关,但我可以尝试。
  • 是的,我提到的声明是using var response = .....
  • 会不会是远程服务器/API 限制了你?
  • @Ogglas - 您还可以编写重试逻辑以防暂时失败。您可以编写代码变体或使用 Polly 或其他包。
  • HTTP 根据定义是不可靠的。网络不可靠。服务不可靠。您应该始终期望请求失败并且必须重试。这就是您需要 Polly 的原因

标签: c# .net asynchronous .net-core async-await


【解决方案1】:

问题可能是由socket exhaustion 引起的。这是 HttpClient 的一个已知问题,解决方案是使用 HttpClientFactory。我没有对此进行测试,但这里是您的 MyService 类的快速重写:

public class MyService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public MyService(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory ??
            throw new ArgumentNullException(nameof(httpClientFactory));
    }

    private async Task<List<string>> GetValuesFromSearch(string search)
    {
        var _httpClient = _httpClientFactory.CreateClient("MyClient");

        _httpClient.BaseAddress = new Uri("https://example.com/");
        _httpClient.Timeout = TimeSpan.FromMinutes(5);

        // You could also set the above in Startup.cs or wherever you add your services:
        //services.AddHttpClient("MyClient", c => {
        //    c.BaseAddress = new Uri("https://example.com/");
        //    c.Timeout = TimeSpan.FromMinutes(5);
        //});

        //Exception is thrown here
        var response = await _httpClient.GetAsync("search/" + search);

        using var responseStream = await response.Content.ReadAsStreamAsync();

        response.EnsureSuccessStatusCode();

        var searchResultList = await JsonSerializer.DeserializeAsync
            <List<string>>(responseStream);

        return searchResultList;
    }
}

【讨论】:

    【解决方案2】:

    如果您的请求未能返回 HttpResponseMessage,HttpClient 将把它作为 TaskCancelledException 类型的内部异常堆栈跟踪抛出。要确认,请尝试使用 Polly 添加 TimeoutAsync 策略;异常应更改为 TimeOutRejectedException。

    在类似的用例中,我发现的最佳解决方案是:

    private async Task<List<string>> GetValuesFromSearch(string search)
        {
            try
                {
                    //Exception is thrown here
                    var response = await _httpClient.GetAsync("search/" + search);
    
                    var responseStream = await response.Content.ReadAsStreamAsync();
    
                    response.EnsureSuccessStatusCode();
    
                    var searchResultList = await JsonSerializer.DeserializeAsync
                        <List<string>>(responseStream);
    
                    return searchResultList;
                }
            catch (Exception ex)
                {
                    // Log the exception. 
                    // Do what you want, or return null and handle a dropped request in your calling method.
                }
        }
    

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 2014-02-17
      • 2014-07-03
      • 1970-01-01
      • 2017-08-22
      • 1970-01-01
      相关资源
      最近更新 更多