Polly Resilience 上下文为空

问题描述 投票:0回答:1

我正在为我的 .NET Core Web Api 实现弹性,并且我需要一种使用

ResilienceContext
传递值的方法,但当我调试时,
args.Context.Properties
始终为空。我不知道我错过了什么。

我正在使用

Microsoft.Extensions.Http.Resilience
使用
Polly

这是我的代码:

public static class ResilienceKeys
{
    public static readonly ResiliencePropertyKey<string> LocationId = new("location-id");
}
// WordPressService.cs

public class WordPressService : IWordPressService
{
    private readonly HttpClient _httpClient;
    private readonly ResiliencePipelineProvider<string> _pipelineProvider;

    public WordPressService(HttpClient httpClient, ResiliencePipelineProvider<string> pipelineProvider)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri("https://api.example.com/");

        _pipelineProvider = pipelineProvider;
    }

    public async Task<string> GetBannersByLocationAsync(int locationId, CancellationToken cancellationToken)
    {
        var pipeline = _pipelineProvider.GetPipeline<HttpResponseMessage>("my-pipeline");

        var context = ResilienceContextPool.Shared.Get(cancellationToken);

        context.Properties.Set(ResilienceKeys.LocationId, locationId.ToString());

        var response = await pipeline.ExecuteAsync<HttpResponseMessage, ResilienceContext>(
            async (context, cancellationToken) =>
            {
                // Simulate an error for testing purposes
                await Task.Delay(100, cancellationToken);
                var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
                return response;
            },
            context,
            cancellationToken);

        return await response.Content.ReadAsStringAsync(cancellationToken);
    }
}

我还使用依赖注入来添加 ResiliencePipeline,并使用

RetryStrategy
FallbackStrategy
从数据库获取内容,以防 WordPress 服务器没有响应

// Program.cs

builder.Services.AddResiliencePipeline<string, HttpResponseMessage>("my-pipeline", (pipelineBuilder, context) =>
{
    var predicateBuilder = new PredicateBuilder<HttpResponseMessage>()
        .Handle<HttpRequestException>()
        .HandleResult(r => r.StatusCode >= HttpStatusCode.InternalServerError);

    pipelineBuilder
    .AddFallback(new FallbackStrategyOptions<HttpResponseMessage>()
    {
        ShouldHandle = predicateBuilder,
        FallbackAction = async (args) =>
        {
            if (args.Context.Properties.TryGetValue(ResilienceKeys.LocationId, out var data))
            {
                Console.WriteLine("FallbackAction, Location Id: {0}", data);
            }

            var fallbackResponse = new HttpResponseMessage()
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("Fallback content")
            };

            return Outcome.FromResult(fallbackResponse);
        }
    })
    .AddRetry(new RetryStrategyOptions<HttpResponseMessage>()
    {
        ShouldHandle = predicateBuilder,
        Delay = TimeSpan.FromSeconds(2),
        MaxRetryAttempts = 2,
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,
    });
});
c# .net-core dotnet-httpclient polly
1个回答
0
投票

简短的回答是您使用了错误的

ExecuteAsync
重载。你应该使用这个:

var response = await pipeline.ExecuteAsync<HttpResponseMessage>(
    async ctx =>
    {
        // Simulate an error for testing purposes
        await Task.Delay(100, ctx.CancellationToken);
        return new HttpResponseMessage(HttpStatusCode.InternalServerError);
    },
    context);

超载,你所使用的,没有利用

Context
。如果你看到方法签名,那就很清楚了。

public async ValueTask<TResult> ExecuteAsync<TResult, TState>(
    Func<TState, CancellationToken, ValueTask<TResult>> callback,
    TState state,
    CancellationToken cancellationToken = default)

您已将

Context
作为
state
对象传递。 这里我们记录了
Context
State
之间有什么区别。

目前不存在预期会出现

CancellationToken
Context
的此类过载。 但你根本不需要。

每当您从

Context
请求
ResilienceContextPool
时,传递的
CancellationToken
就会附加到检索到的
Context
。如果您查看建议解决方案的代码片段,您可以发现
CancellationToken
是通过
Context
(
ctx.CancellationToken
) 访问的。

因此,您应该使用以下重载

public async ValueTask<TResult> ExecuteAsync<TResult>(
    Func<ResilienceContext, ValueTask<TResult>> callback,
    ResilienceContext context)

作为旁注,请不要忘记归还借用的弹性上下文到池中:

ResilienceContextPool.Shared.Return(context);
© www.soinside.com 2019 - 2024. All rights reserved.