为什么在使用Polly进行重试时,HttpClient在后续重试中仍然失败?

4

我有几种需要某种形式的互联网连接的方法。如果连接失败,我希望在失败之前重试该方法一段时间。由于应用程序可以在等待成功响应时继续正常运行,因此我想以异步方式执行此操作。

我使用Polly(5.3.1)实施异步重试逻辑,利用Tasks

我通过禁用我的Wi-Fi并在重试窗口期间启用它来模拟断开连接。我希望在重新尝试后重新启用连接后该方法会成功,但我看到该方法仍然会像连接已断开一样抛出HttpRequestException,直到重试完成,然后才将其抛给调用者。
如果我像平常一样启用我的Wi-Fi,则该方法立即成功。

// Get the HTML of a web page 'async'
public async Task<string> GetHtmlAsync(string url)
{
    using (var client = new HttpClient())
    using (var response = await client.GetAsync(url))
    {
        response.EnsureSuccessStatusCode();
        using (var content = response.Content)
        {
            return await content.ReadAsStringAsync();
        }
    }
}

// Wrapper for Polly to create an async retry policy
public async Task<TResult> RetryAsync<TResult, TException>(Task<TResult> task, int retries, int seconds) where TException : Exception
{
    return await Policy
               .Handle<TException>()
               .WaitAndRetryAsync(retries, wait => TimeSpan.FromSeconds(seconds))
               .ExecuteAsync(async () => await task);
}

// Call the method, it will retry 12 times with a gap of 5 seconds between tries
var html = await RetryAsync<string, HttpRequestException>(GetHtmlAsync("https://www.google.co.uk"), 12, 5);

为什么在后续重试期间,即使我的连接已启用并正常工作,方法仍然持续失败?

你不应该创建太多的HttpClient实例。长期来看这会导致问题。创建一个实例并重复使用它。 - Nkosi
@NKosi,你是基于我在这里展示的5秒重试还是通常重新创建HttpClient就很昂贵?我应该指出,这个重试只是为了排除故障而短暂的,实际上我可能会在尝试之间等待至少30秒。 - Equalsk
https://dev59.com/VoDba4cB1Zd3GeqPC1Vw - Nkosi
这更多关于为每个客户端创建的底层TCP连接。 - Nkosi
@Nkosi 太棒了,谢谢。我不知道这个。我会去了解一下的。 - Equalsk
1个回答

5

由于您没有重新执行任何内容,因此它会随后失败。 Task 表示异步执行的未来结果。订阅它只会给你结果,它不会重新运行代码。

想象一下你启动了一个蛋计时器,你可以把它传递给每个人,大家都可以看到它是否完成,但如果它已经完成,他们将立即看到。在您的情况下,由于第一次失败,它立即失败了后续检查。

您想要的是重试调用:

public async Task<string> GetHtmlAsync(string url)
{
    using (var client = new HttpClient())
    using (var response = await client.GetAsync(url))
    {
        response.EnsureSuccessStatusCode();
        using (var content = response.Content)
        {
            return await content.ReadAsStringAsync();
        }
    }
}

// Wrapper for Polly to create an async retry policy
public async Task<TResult> RetryAsync<TResult, TException>(
    Func<Task<TResult>> taskInitiator, int retries, int seconds) where TException : Exception
{
    return await Policy
               .Handle<TException>()
               .WaitAndRetryAsync(retries, wait => TimeSpan.FromSeconds(seconds))
               .ExecuteAsync(async () => await taskInitiator());
}

// Call the method, it will retry 12 times with a gap of 5 seconds between tries
var html = await RetryAsync<string, HttpRequestException>(
    () => GetHtmlAsync("https://www.google.co.uk"), 12, 5);

太棒了,这完全有道理。我很烦恼它如此明显,我觉得自己已经对代码失去了敏感。 - Equalsk
+1。与此几乎相同的问题最终在这里被揭示:https://stackoverflow.com/questions/46414976/c-sharp-polly-async-await-wait-for-user-confirmation-before-retry/46421518#46421518。但是通过在第一个版本的q中发布您的完整代码,equalsk,您立即得到了正确的答案。@richardszalay:用蛋计时器做比喻很棒! - mountain traveller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接