c#: перезапустить задачу Asyn c через определенное время до завершения. - PullRequest
6 голосов
/ 27 мая 2020

Итак, я работал над приложением, которое потребляет запросы REST API, однако по какой-то причине API случайно не отвечает (иногда он дает ответ в течение 3 секунд, а иногда запрос занимает так много времени, что выдает a timeOutexception), поэтому всякий раз, когда я использую вызов, я использую этот код для перезапуска вызова, если в течение определенного времени не получен ответ:

bool taskCompletion = false;
        while(taskCompletion == false)
        {
            try
            {
                using (CancellationTokenSource cts = new CancellationTokenSource())
                {
                    cts.CancelAfter(timeSpan);
                    await task(cts.Token);
                    taskCompletion = true;
                }
            }
            catch (OperationCanceledException)
            {
                taskCompletion = false;
            }
        }

и один из моих запросов API следующий:

 public static async Task<Result> task(CancellationToken ct)
    {
        string Url = baseurl
        ApiHelper instance = new ApiHelper();

        using (HttpResponseMessage response = await instance.ApiClient.GetAsync(Url, ct))
        {
            if (response.IsSuccessStatusCode)
            {
                var x = await response.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject<ResultL>(x);
                if (result.result.Count() != 0)
                    return result.result[0];
                else
                    return null;
            }
            return null;
        }
    }

Однако я не думаю, что использование try-catch каждый раз для каждого из различных запросов API, потребляемых кодом, является лучшим решением, любая помощь в том, как улучшить мой код, будет высоко оценена!

Ответы [ 2 ]

3 голосов
/ 27 мая 2020

Рассматривали ли вы использование библиотеки отказоустойчивости? Одним из примеров для. net является Polly. https://github.com/App-vNext/Polly

Это полезно, потому что вы можете легко настроить счетчик повторов или тайм-аут, а также резервный лог c для определенного типа исключений.

Там также очень полезная статья Скотта Хансельмана по этому поводу: https://www.hanselman.com/blog/AddingResilienceAndTransientFaultHandlingToYourNETCoreHttpClientWithPolly.aspx

Я использовал его раньше, и он сделал мой код очень чистым и простым в управлении, поскольку все политики находятся в одном место, а не часть обработчика HTTP-ответа. Вы также можете иметь отдельную политику для каждого HTTP-запросчика или клиента, если необходимо.

1 голос
/ 28 мая 2020

Вся предпосылка этой проблемы заключается в том, что асинхронная операция, которая перестала отвечать, все равно будет отвечать на запрос об отмене через предоставленный CancellationToken. Я немного скептичен c о реальной применимости этого предположения, но в любом случае вот метод AwaitCancelRetry, который автоматически отменяет и повторяет асинхронную операцию, если это занимает слишком много времени:

public static async Task<T> AwaitCancelRetry<T>(
    Func<CancellationToken, Task<T>> function, TimeSpan timeout,
    int maxAttempts, CancellationToken externalToken = default)
{
    for (int i = 0; i < maxAttempts; i++)
    {
        using (var cts = CancellationTokenSource
            .CreateLinkedTokenSource(externalToken, default))
        {
            cts.CancelAfter(timeout);
            try
            {
                return await function(cts.Token); // Continue on captured context
            }
            catch (OperationCanceledException ex)
                when (ex.CancellationToken == cts.Token
                    && !externalToken.IsCancellationRequested)
            {
                continue;
            }
        }
    }
    throw new TimeoutException();
}

// Non generic version
public static Task AwaitCancelRetry(
    Func<CancellationToken, Task> function, TimeSpan timeout,
    int maxAttempts, CancellationToken externalToken = default)
    => AwaitCancelRetry<object>(
        async ct => { await function(ct).ConfigureAwait(false); return null; },
        timeout, maxAttempts, externalToken);

Пример использования:

private static HttpClient _httpClient;

public static Task<string> GetDataAsync(string url, CancellationToken token)
{
    return AwaitCancelRetry(async ct =>
    {
        using (HttpResponseMessage response = await _httpClient.GetAsync(url, ct))
        {
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            return null;
        }
    }, TimeSpan.FromSeconds(10), maxAttempts: 3, token);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...