我有一个.NET 8 API,使用Polly来恢复Azure SQL和Microsoft Curve等外部服务的弹性.我当前的实现使用自定义再试和断路器策略,如下所示:

PollyExtensions.cs:

public static class PollyExtensions
{
    public static AsyncRetryPolicy CreateRetryPolicy(this ILogger logger, string infraService)
    {
        var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5);

        return Policy
            .Handle<SqlException>()
            .Or<Exception>()
            .Or<TimeoutException>()
            .WaitAndRetryAsync(delay,
                onRetry: (exception, timespan, retryAttempt, context) =>
                {
                    logger.LogWarning(exception, "Error talking to {infraService}, Error: {Message}, will retry after {timeSpan}. Retry attempt {retryCount} ",
                        infraService, exception.Message, timespan, retryAttempt);
                });
    }

    public static AsyncCircuitBreakerPolicy CreateCircuitBreakerPolicy()
    {
        return Policy
            .Handle<Exception>()
            .CircuitBreakerAsync(
                exceptionsAllowedBeforeBreaking: 5,
                durationOfBreak: TimeSpan.FromMinutes(1)
            );
    }
}

在探索.NET 8中的新功能时,我发现了实现弹性的AddStandardResilienceHandlerAddStandardHedgingHandler.

References :

https://devblogs.microsoft.com/dotnet/building-resilient-cloud-services-with-dotnet-8/

https://juliocasal.com/blog/Building-Microservices-With-Dotnet-8

有人可以提供代码示例来演示如何在我的GraphSvcClient类的上下文中使用AddStandardResilienceeds和AddStandardHedgingeeds来进行外部依赖项调用?

与自定义策略相比,使用这些新功能时是否有任何具体的注意事项?

GraphSvcClient.cs

public sealed class GraphSvcClient(ICacheProvider cacheProvider,
    IAzureAuthTokenService azureAuthTokenService,    
    ILogger<GraphSvcClient> logger) : IGraphSvcClient
{
    private readonly ICacheProvider _cacheProvider = cacheProvider.IsNotNull();
    private readonly IAzureAuthTokenService _azureAuthTokenService = azureAuthTokenService.IsNotNull();
    private readonly ILogger<GraphSvcClient> _logger = logger.IsNotNull();
    
    /// <summary>
    /// Get Microsoft Graph graphClient
    /// </summary>
    /// <returns></returns>
    public async Task<GraphServiceClient> GetGraphServiceClientAsync()
    {
        var accessToken = await GetAccessTokenAsync();

        var retryPolicy = _logger.CreateRetryPolicy(infraService: "MicrosoftGraph");
        var circuitBreakerPolicy = PollyExtensions.CreateCircuitBreakerPolicy();

        var combinedPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);

        var graphServiceClient = await combinedPolicy.ExecuteAsync(async () =>
        {
            var client = new GraphServiceClient(new DelegateAuthenticationProvider(async (req) =>
            {
                req.Headers.Authorization = new AuthenticationHeaderValue(JwtBearerDefaults.AuthenticationScheme, accessToken);
                await Task.CompletedTask;
            }));
            return client;
        });

        return graphServiceClient;
    }
}

我正在考虑将Polly及其相关的nuget包更新到最新的兼容版本,以利用.NET 8中的最新功能.

项目中使用的Nuget包列表:

<PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.0" />

推荐答案

我看到了一些误解,所以让我try 澄清一些事情.

Microsoft.Extensions.Http.XYZ packages

波莉的设计是领域不可知的.换句话说,它可以用于与HTTP呼叫相关的瞬时故障处理以及数学计算.这些策略(在V7的情况下)足够通用,可以用于多种用例.

一个常见的用例是基于HttpClient的网络通信.由于Polly的受欢迎程度,首先是Polly社区,然后是Microsoft发布了几种助手 struct 和方法,以简化Polly与HttpClient的集成和使用.

对于V7 API,Microsoft发布了Microsoft.Extensions.Http.Polly,它可以帮助您通过AddPolicyHandler扩展方法使用任意弹性策略装饰所有HttpClient呼叫.

对于V8 API,Microsoft发布了Microsoft.Extensions.Http.Resilience包,该包可以帮助您使用预定义的弹性管道或任何任意的弹性策略装饰所有HttpClient呼叫.前者可以通过AddStandardResilienceHandlerAddStandardHedgingHandler扩展方法完成,而后者可以通过AddResilienceHandler扩展方法完成.

将代码迁移到V8

重试

您的再试策略可以轻松迁移到V8.很高兴exponential backoff algorithm is supported natively.这意味着您也不必使用依赖于某些contrib依赖.

return new ResiliencePipelineBuilder().Add重试(new()
{
    ShouldHandle = new PredicateBuilder()
        .Handle<SqlException>()
        .Handle<TimeoutException>()
        .Handle<Exception>(),
    BackoffType = DelayBackoffType.Exponential,
    UseJitter = true,
    Max重试Attempts = 5,
    Delay = TimeSpan.FromSeconds(1),
    On重试 = args =>
    {
        logger...
        return default;
    }
})
.Build();

请注意,如果您在V7的情况下有Or<Handle>条款或在V8的情况下有Handle<Exception>条款,那么所有其他Handle/Or条款都是不必要的.

断路器

目前是基于连续故障计数的V8 API does not support this standard/classic 断路器模型.V8支持基于抽样的模型,类似于V7's Advanced 断路器.

因此,简而言之,您不能按原样迁移它.如果您想使用V8 API,则必须使用sampling based approach.

Policy.Wrap

在V7 API中组合多个策略可以在several ways内完成,但建议的解决方案是使用Policy.Wrap{Async}.

对于V8,您必须使用ResiliencePipelineBuilderadd multiple strategies to your pipeline.

ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
    .Add重试(new()
    {
        ...
    })
    .AddCircuitBreaker(new()
    {
        ...
    })
    .Build();

In case of V8 the registration order也至关重要,就像V7一样.

您的图表服务

您最初的政策与https无关.您的V8策略也不必与https相关.这意味着不需要使用AddStandardResilienceHandlerAddStandardHedgingHandler.

根据共享代码片段,我无法判断GraphServiceClient是否是输入的HttpClient(但我假设不是).因此,您只需将再试和断路器策略迁移到V8,就能收获V8良好性.

Csharp相关问答推荐

System.Text.Json Utf8JsonReader正在读取数组值两次

在WPF.NET 6中使用C++/WinRT组件(但实际上是任何WinRT组件)

使用yaml将Azure函数代码部署到FunctionApp插槽时出现问题(zip未找到)

ASP.NET Core:如何在IPageFilter中注入ApplicationDbContext

如何从HttpContext获取请求正文

通过条件列表删除/更新EF Core 7中的实体的有效方法

如何使用XmlSerializer反序列化字符串数组?

从ASP.NET Core中的枚举字段填充 Select 选项时,将默认的第一个选项添加为 Select 元素

如何将MongoDB序列化程序设置为内部对象属性

BlockingCollection T引发意外InvalidOperationException

在不添加不必要的尾随零的情况下本地化浮点型?

Linux Docker上的.NET6在某个时间抛出后,在加密操作期间发生System.Security.Cryptography.CryptographicException:错误

如何将FindAll()与Nuget包Interop.UIAutomationClient一起使用

try 链接被委派者(多播委托)时,无法将获取运算符应用于类型为';方法组&39;和方法组';的操作数

Azure函数-在外部启动类中生成配置时出错

如何在Akka.NET中重新启动执行元时清除邮箱

我可以强制System.Text.Json.JsonSerializer以非递归方式工作吗?

我是否应该注销全局异常处理程序

C#中COM对象的实际地址

如何在C#中用Serilog记录类路径、方法名和行编号