c#: 在完成一个Async任务之前,经过一定时间后重新启动该任务。

问题描述 投票:2回答:1

所以,我一直在做一个消耗REST API请求的应用,然而,由于某些原因,API会随机地变得无响应(有时它在3秒内给出响应,有时请求会花很长时间,以至于它抛出一个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请求是最好的解决方案, 任何关于如何改进我的代码的帮助都将是非常感激的!

c# rest asynchronous servicenow
1个回答
3
投票

你是否考虑过使用一个容错库?.net的一个例子是Polly。https:/github.comApp-vNextPolly。

这很有帮助,因为你可以很容易地配置重试次数或超时,以及某些类型的异常的回退逻辑。

Scott Hanselman也有一篇非常有用的文章。https:/www.hanselman.comblogAddingResilienceAndTransientFaultHandlingToYourNETCoreHttpClientWithPolly.aspx

我以前用过它,它让我的代码变得超级干净和容易管理,因为所有的策略都在一个地方,而不是http响应处理器的一部分。如果需要的话,你也可以为每个不同的http请求者或客户端制定一个单独的策略。


1
投票

这个问题的整个前提是,一个异步操作已经变得无响应,仍然会通过提供的取消请求响应 CancellationToken. 我对这一假设在现实生活中的适用性持怀疑态度,但无论如何,这里有一个。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);
}
© www.soinside.com 2019 - 2024. All rights reserved.