所以,我一直在做一个消耗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请求是最好的解决方案, 任何关于如何改进我的代码的帮助都将是非常感激的!
你是否考虑过使用一个容错库?.net的一个例子是Polly。https:/github.comApp-vNextPolly。
这很有帮助,因为你可以很容易地配置重试次数或超时,以及某些类型的异常的回退逻辑。
Scott Hanselman也有一篇非常有用的文章。https:/www.hanselman.comblogAddingResilienceAndTransientFaultHandlingToYourNETCoreHttpClientWithPolly.aspx
我以前用过它,它让我的代码变得超级干净和容易管理,因为所有的策略都在一个地方,而不是http响应处理器的一部分。如果需要的话,你也可以为每个不同的http请求者或客户端制定一个单独的策略。
这个问题的整个前提是,一个异步操作已经变得无响应,仍然会通过提供的取消请求响应 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);
}