Возможный тупик HttpClient - PullRequest
0 голосов
/ 12 июля 2019

У меня есть класс декоратора, который добавляет возможность ограничивать число запросов http для обращения к API:

public class RateLimitedHttpClient : IHttpClient
{
    public RateLimitedHttpClient(System.Net.Http.HttpClient client)
    {
        _client = client;
        _client.Timeout = TimeSpan.FromMinutes(30);
        //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
    }
    public async Task<string> ReadAsync(string url)
    {
        if (!_sw.IsRunning)
            _sw.Start();

        await Delay();

        using var response = await _client.GetAsync(url);

        return await response.Content.ReadAsStringAsync();
    }

    private async Task Delay()
    {
        var totalElapsed = GetTimeElapsedSinceLastRequest();

        while (totalElapsed < MinTimeBetweenRequests)
        {
            await Task.Delay(MinTimeBetweenRequests - totalElapsed);
            totalElapsed = GetTimeElapsedSinceLastRequest();
        };

        _timeElapsedOfLastHttpRequest = (int)_sw.Elapsed.TotalMilliseconds;
    }

    private int GetTimeElapsedSinceLastRequest()
    {
        return (int)_sw.Elapsed.TotalMilliseconds - _timeElapsedOfLastHttpRequest;
    }

    private readonly System.Net.Http.HttpClient _client;
    private readonly Stopwatch _sw = new Stopwatch();
    private int _timeElapsedOfLastHttpRequest;
    private const int MinTimeBetweenRequests = 100;
}

Однако я замечаю, что в строке, указанной ниже, я получаю сообщение в отладчикеэто говорит о том, что the next statement will execute when the current thread returns.

var epsDataPoints = await _downloader.GetEPSData(cik);

foreach (var eps in epsDataPoints)
{
     // getting VS2019 debugger message here!
     // assuming that the line above is deadlocking....
     Console.WriteLine($"{cik} :: {eps.DateInterval} :: {eps.EPS}");
}

Когда я открываю диспетчер задач, пропускная способность сети становится равной 0, и все останавливается с приложением, отличным от Console.WriteLine выше.

Класс EPSDownloader, использующий IHttpClient, приведен ниже:

public class EPSDownloader
{
    public EPSDownloader(IHttpClient client)
    {
        _client = client;
    }

    public async Task<IEnumerable<EPSDataPoint>> GetEPSData(int cik)
    {
        var epsDataPoints = new Dictionary<LocalDate, EPSDataPoint>();
        var reportLinks = await GetReportLinks(cik);

        foreach (var reportLink in reportLinks)
        {
            var xbrlLink = await GetXBRLLink(reportLink);
            var epsData = await GetEPSData(xbrlLink);

            foreach (var eps in epsData)
            {
                if (!epsDataPoints.ContainsKey(eps.DateInterval.End))
                    epsDataPoints.Add(eps.DateInterval.End, eps);
            }
        }

        var list = epsDataPoints.OrderBy(d => d.Key).Select(e => e.Value).ToList();

        return list;
    }

    private async Task<IList<string>> GetReportLinks(int cik)
    {
        // move this url elsewhere
        var url = "https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=" + cik +
                  "&type=10&dateb=&owner=include&count=100";

        var srBody = await _client.ReadAsync(url); // consider moving this to srPage
        var srPage = new SearchResultsPage(srBody);

        return srPage.GetAllReportLinks();
    }

    private async Task<string> GetXBRLLink(string link)
    {
        var url = SEC_HOSTNAME + link;

        var fdBody = await _client.ReadAsync(url);
        var fdPage = new FilingDetailsPage(fdBody);

        return fdPage.GetInstanceDocumentLink();
    }

    private async Task<IList<EPSDataPoint>> GetEPSData(string xbrlLink)
    {
        var xbrlBody = await _client.ReadAsync(SEC_HOSTNAME + xbrlLink);
        var xbrlDoc = new XBRLDocument(xbrlBody);

        return xbrlDoc.GetAllQuarterlyEPSData();
    }

    private readonly IHttpClient _client;
    private const string SEC_HOSTNAME = "https://www.sec.gov";
}

Кажется, что есть проблема с HttpClient, но я не знаю почему.Не выдается никаких исключений, но я иногда вижу, что потоки закрылись с кодом 0.

Обновление: я фактически перезапустил свой компьютер во время работы приложения, и он снова начал нормально работать в течение примерно 20 минут, прежде чем задачаДиспетчер показал 0 для скорости сети и приложение просто сидело там.

...