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

无法将类型“Polly.CircuitBreaker.AsyncCircuitBreaker”隐式转换为“Polly.Policy”

如何解决无法将类型“Polly.CircuitBreaker.AsyncCircuitBreaker”隐式转换为“Polly.Policy”

我正在尝试为我的 Rest 客户创建一个组合弹性策略,为此我写了以下内容

private static Policy circuitBreakerPolicy = Policy
     .Handle<TimeoutException>()
     .CircuitBreakerAsync(3,TimeSpan.FromSeconds(2));

我收到错误

无法将类型 Polly.CircuitBreaker.AsyncCircuitBreaker 隐式转换为 Polly.Policy

我错过了什么?我在网上查了很多参考资料,但我无法对导致此问题的原因做出足够简洁的解释。

用法: 最终,我想将上面的 CircuitBreaker 策略与 WaitandRetry 策略相结合,但我不知道如何提取组合策略:

(这是有效的)

public static IAsyncPolicy<HttpResponseMessage> CreateResiliencePolicy()
{
    var timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromSeconds(180));
 
    var waitAndRetryPolicy = Polly.Policy
        .Handle<HttpRequestException>()
        .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
        .WaitAndRetryAsync(3,retryAttempt => TimeSpan.FromSeconds(Math.Pow(3,retryAttempt)),(result,timeSpan,context) =>
        {
        });
    
    return timeoutPolicy.WrapAsync(waitAndRetryPolicy);
}

这就是我想要的(不工作):

public static IAsyncPolicy<HttpResponseMessage> CreateResiliencePolicy()
{
    var circuitBreakerPolicy = Policy
        .Handle<TimeoutException>()
        .CircuitBreakerAsync(3,TimeSpan.FromSeconds(2));

    var waitAndRetryPolicy = Polly.Policy
        .Handle<HttpRequestException>()
        .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
        .WaitAndRetryAsync(3,context) =>
        {
        });
   
    return circuitBreakerPolicy.WrapAsync(waitAndRetryPolicy);

     //I want to do this instead
     //public static Policy resilientAsyncStrategy =circuitBreakerPolicy.WrapAsync(waitAndRetryPolicy);

     //Then return resilientAsyncStrategy
     //return resilientAsyncStrategy;
}

然后使用返回的策略实例为:

public async Task<IEnumerable<PaymentDetailDto>> GetAsync()
{
    var items = await resilientAsyncStrategy.ExecuteAsync(async () => await client.GetAsync());
    return items;
}

解决方法

Polly 定义了以下四种抽象策略类型:

方法 功能
同步 Policy Policy<TResult>
异步 AsyncPolicy AsyncPolicy<TResult>

因此,在您的情况下,circuitBreakerPolicy 应定​​义如下:

private static AsyncPolicy circuitBreakerPolicy = Policy
     .Handle<TimeoutException>()
     .CircuitBreakerAsync(3,TimeSpan.FromSeconds(2));

每当您想要组合/链接两个(或多个)策略时,您都应该考虑使用 PolicyWrap (reference)。请记住,策略链在 escalation way 中工作,这意味着如果内部策略无法处理问题,那么它会将其传播到下一个外部策略。

另请记住,政策应该相互兼容。因此,如果其中一个是异步的,那么另一个也应该是异步的。如果内部返回一些东西,那么外部也应该这样做。

因此,您的 CreateResiliencePolicy 可能如下所示:

public static AsyncPolicy<HttpResponseMessage> CreateResilienceStrategy()
{
    var circuitBreakerPolicy = Policy<HttpResponseMessage>
        .Handle<TimeoutException>()
        .CircuitBreakerAsync(3,TimeSpan.FromSeconds(2));

    var waitAndRetryPolicy = Polly.Policy
        .Handle<HttpRequestException>()
        .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
        .WaitAndRetryAsync(3,retryAttempt => TimeSpan.FromSeconds(Math.Pow(3,retryAttempt)),(result,timeSpan,context) =>
        {
        });

    return Policy.WrapAsync(circuitBreakerPolicy,waitAndRetryPolicy);
}

还请记住,订购很重要:

  1. Policy.WrapAsync(circuitBreakerPolicy,waitAndRetryPolicy) 你有一个内部重试和一个外部 CB
  2. Policy.WrapAsync(waitAndRetryPolicy,circuitBreakerPolicy) 你有一个内部 CB 和一个外部重试

以下两行是等价的:

circuitBreakerPolicy.WrapAsync(waitAndRetryPolicy);
Policy.WrapAsync(circuitBreakerPolicy,waitAndRetryPolicy)

如果您想更改政策的顺序,那么您的策略将以不同的方式运作。如果您想使用 CB 作为内部策略并将重试作为外部策略,那么您应该修改 waitAndRetryPolicy 以处理 BrokenCircuitException

几个月前,我整理了一个 sample application,它展示了如何逐步设计您的弹性策略。


更新:添加示例代码

测试重试逻辑

我使用以下控制台应用来测试您的重试策略:

private static HttpClient client = new HttpClient();
public static async Task Main(string[] args)
{
    var strategy = CreateResilienceStrategy();
    await strategy.ExecuteAsync(async (ct) =>
        await client.GetAsync("https://httpstat.us/500",ct),CancellationToken.None);
    Console.WriteLine("Finished");
}

public static AsyncPolicy<HttpResponseMessage> CreateResilienceStrategy()
{
    var circuitBreakerPolicy = Policy<HttpResponseMessage>
        .Handle<TimeoutException>()
        .CircuitBreakerAsync(3,TimeSpan.FromSeconds(2),onBreak: (_,__) => Console.WriteLine("Break"),onReset: () => Console.WriteLine("Reset"),onHalfOpen: () => Console.WriteLine("HalfOpen"));

    var waitAndRetryPolicy = Polly.Policy
        .Handle<HttpRequestException>()
        .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
        .WaitAndRetryAsync(3,onRetryAsync: (_,ts,___) =>
        {
            Console.WriteLine($"Retry,penalty: {ts.Seconds} secs");
            return Task.CompletedTask;
        });

    return Policy.WrapAsync(circuitBreakerPolicy,waitAndRetryPolicy);
}

输出:

Retry,penalty: 3 secs
Retry,penalty: 9 secs
Retry,penalty: 27 secs
Finished

如你所见,

  • 它在没有运气的情况下执行了所有重试
  • 它没有触发断路器
  • strategy.ExecuteAsync 返回了状态代码为 500 的响应
  • 它在 Http 调用后继续工作

测试断路器逻辑

因为 CB 策略设置为针对 TimeoutException 触发,所以我将 client.Timeout 中的 Main 设置为 1 毫秒。因此,应用程序因 TaskCanceledException 崩溃。

这样做是因为 HttpClient 在超时的情况下抛出 TaskCanceledException 而不是 TimeoutException。如果您使用波利的超时政策,那么您将收到 TimeoutRejectedException

所以,这是我修改后的代码,以便能够测试 CB

private static HttpClient client = new HttpClient();
public static async Task Main(string[] args)
{
    client.Timeout = TimeSpan.FromMilliseconds(1);
    var strategy = CreateResilienceStrategy();
    try
    {
        await strategy.ExecuteAsync(async (ct) =>
            await client.GetAsync("https://httpstat.us/500",CancellationToken.None);
        Console.WriteLine("Finished");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Failed with " + ex.GetType().Name);
    }    
}

public static AsyncPolicy<HttpResponseMessage> CreateResilienceStrategy()
{
    var circuitBreakerPolicy = Policy<HttpResponseMessage>
        .Handle<OperationCanceledException>()
        .Or<TimeoutRejectedException>()
        .CircuitBreakerAsync(1,onHalfOpen: () => Console.WriteLine("HalfOpen"));

    var waitAndRetryPolicy = Polly.Policy
        .Handle<HttpRequestException>()
        .Or<OperationCanceledException>()
        .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
        .WaitAndRetryAsync(3,penalty: {ts.Seconds} secs");
            return Task.CompletedTask;
        });

    return Policy.WrapAsync(waitAndRetryPolicy,circuitBreakerPolicy);
}

修改:

  • 将超时设置为 1 毫秒
  • 将 CB 的连续失败计数从 3 更改为 1
  • 更改链接:重试外部,cb 内部
  • 为这两个政策添加了 OperationCanceledException 个条件

输出

Break
Retry,penalty: 3 secs
HalfOpen
Break
Retry,penalty: 9 secs
HalfOpen
Break
Retry,penalty: 27 secs
HalfOpen
Break
Failed with OperationCanceledException

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