首页 > 解决方案 > .NET Core 3.1 HttpClient 间歇性抛出异常:SocketException: I/O 操作已被中止,因为线程退出或

问题描述

我间歇性地得到以下例外:

IOException:无法从传输连接读取数据:I/O 操作已因线程退出或应用程序请求而中止。

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

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

我的服务.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);
}

标签: c#.netasynchronous.net-coreasync-await

解决方案


该问题可能是由于套接字 耗尽。这是 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;
    }
}

推荐阅读