微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 .Net Core 中捕获 Polly 中的最后一个异常?

如何解决在 .Net Core 中捕获 Polly 中的最后一个异常?

我正在使用 Polly (Microsoft.Extensions.Http.Polly) 和 .net core 的这种配置(使用无效的 URL,用于测试):

private static void RegisterServices()
{
    var collection = new ServiceCollection();
    var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2); // Timeout for an individual try
 

    collection.AddHttpClient<INetworkService,NetworkService>(url=>
             {
                 url.BaseAddress = new Uri("http://www.google.com:81"); //test bad url
             })
             .AddPolicyHandler(GetRetryPolicy()) 
             .AddPolicyHandler(timeoutPolicy); ;

    _serviceProvider = collection.BuildServiceProvider();
}

GetRetryPolicy 在哪里:

private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode != HttpStatusCode.OK)
        .Or<TimeoutRejectedException>()
        .Or<TaskCanceledException>()
        .Or<OperationCanceledException>()
        .WaitAndRetryAsync(3,retryAttempt =>
        {
        return  TimeSpan.FromSeconds(2);
        },onRetry: (response,delay,retryCount,context) =>
            {
              Console.WriteLine($"______PollyAttempt_____ retryCount:{retryCount}  ");
            });
}

输出是:

_PollyAttempt retryCount:1
_PollyAttempt retryCount:2
_PollyAttempt retryCount:3
异常:(TimeoutException) 通过 TimeoutPolicy 异步执行的委托没有在超时时间内完成。

我想在上次尝试失败后发送一封电子邮件

问题:

我怎样才能捕捉到那个最终异常?是否有任何内置机制可以让我知道 Polly 失败了?

(我目前的工作代码https://pastebin.pl/view/a2566d51

解决方法

让我们从一个根本不使用 Polly 的简单设置开始:

private static void RegisterServices()
{
    var collection = new ServiceCollection();
    collection.AddHttpClient<INetworkService,NetworkService>(sonol =>
    {
        sonol.BaseAddress = new Uri("http://www.google.com:81");
    });

    _serviceProvider = collection.BuildServiceProvider();
}
  • 100 秒(HttpClient 的默认超时)后,它将失败并显示 TaskCanceledException。换句话说,HttpClient 取消了请求,因为它没有收到任何响应。

现在让我们稍微改变一下 HttpClient 设置:

private static void RegisterServices()
{
    var collection = new ServiceCollection();
    collection.AddHttpClient<INetworkService,NetworkService>(sonol =>
    {
        sonol.Timeout = TimeSpan.FromSeconds(3); // << NEW CODE
        sonol.BaseAddress = new Uri("http://www.google.com:81");
    });

    _serviceProvider = collection.BuildServiceProvider();
}
  • 3 秒后 HttpClient 取消请求并抛出 TaskCanceledException

现在,注释掉这个超时设置,让我们设置超时策略:

private static void RegisterServices()
{
    var collection = new ServiceCollection();
    var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2);

    collection.AddHttpClient<INetworkService,NetworkService>(sonol =>
    {
        //sonol.Timeout = TimeSpan.FromSeconds(3);
        sonol.BaseAddress = new Uri("http://www.google.com:81");
    })
    .AddPolicyHandler(timeoutPolicy); // << NEW CODE

    _serviceProvider = collection.BuildServiceProvider();
}
  • 2 秒后,Polly 的 TimeoutPolicy 取消请求并抛出 TimeoutRejectedException
    • 它的 InnerException 是原来的 TaskCanceledException

最后让我们添加重试策略:

private static void RegisterServices()
{
    var collection = new ServiceCollection();
    var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2);

    collection.AddHttpClient<INetworkService,NetworkService>(sonol =>
    {
        //sonol.Timeout = TimeSpan.FromSeconds(3);
        sonol.BaseAddress = new Uri("http://www.google.com:81");
    })
    .AddPolicyHandler(Policy.WrapAsync(GetRetryPolicy(),timeoutPolicy)); // << NEW CODE
    //.AddPolicyHandler(timeoutPolicy);

    _serviceProvider = collection.BuildServiceProvider();
}
  • 14 秒后(3+1 2 秒长请求和 3 2 秒长处罚)重试策略抛出原始异常,即innerPolicy 的TimeoutRejectedException
    • 该异常的内部是 HttpClient 的 TaskCanceledException

更新:捕捉评论的精髓

我知道所有尝试都失败的点在哪里?

当您的 Polly 装饰 HttpClient 抛出 TimeoutRejectedException 时,您可以确定所有尝试都失败了。因此,您应该在类型化的客户端内使用 try-catch 包装 GetAsync

我应该检查异常以查看它是 timeoutException 吗?

如果 url 格式错误,它将抛出不同的异常。因此,如果您捕获 TimeoutRejectedException,则意味着下游不可用或已过载。

我是否需要捕获第一个 TimeoutRejectedException 异常才能识别退休失败?

从消费者的角度来看,会有一个例外。重试策略会抛出它

  • 重试次数用完时
  • 或者当它没有配置为处理它时。
    • 所有未通过 Handle<>Or<> 调用明确列出的异常都被视为未处理。这意味着如果没有任何重试,策略就会抛出该错误。

换句话说,如果客户端在给定的时间段内未收到来自下游系统的答复,则将抛出 TimeoutRejectedException。但如果存在网络问题,它也可能会抛出 HttpRequestException

  • 如果重试被配置为处理该问题,那么您可以确定如果它被抛出,那么所有重试尝试都失败了。
  • 如果未配置,则无需任何重试就会抛出 HttpRequestException

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。