.NET Core Web Api中Polly ResilienceContext属性为空问题排查
问题:ResilienceContext传递值时Properties始终为空
在.NET Core Web Api中使用基于Polly的Microsoft.Extensions.Http.Resilience实现弹性机制,尝试通过ResilienceContext传递值,但调试时发现args.Context.Properties始终为空。相关代码如下:
定义ResilienceKeys类
public static class ResilienceKeys { public static readonly ResiliencePropertyKey<string> LocationId = new("location-id"); }
WordPressService中的调用逻辑
// 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); ResilienceContextPool.Shared.Return(context); return await response.Content.ReadAsStringAsync(cancellationToken); } }
Program.cs中配置ResiliencePipeline的代码
// 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, }); });
原因分析
问题出在ExecuteAsync的调用方式上:你使用的ExecuteAsync<HttpResponseMessage, ResilienceContext>重载中,第二个参数是状态对象(State),而非传递ResilienceContext的正确参数。这个重载会自动创建新的默认ResilienceContext,不会复用你设置了Properties的实例,导致策略(Fallback/Retry)无法获取到你传入的值。
解决方案
修改WordPressService中的ExecuteAsync调用,使用专门接受ResilienceContext的重载:
public async Task<string> GetBannersByLocationAsync(int locationId, CancellationToken cancellationToken) { var pipeline = _pipelineProvider.GetPipeline<HttpResponseMessage>("my-pipeline"); var context = ResilienceContextPool.Shared.Get(cancellationToken); try { context.Properties.Set(ResilienceKeys.LocationId, locationId.ToString()); // 使用接受ResilienceContext的ExecuteAsync重载 var response = await pipeline.ExecuteAsync(async (ctx) => { await Task.Delay(100, ctx.CancellationToken); return new HttpResponseMessage(HttpStatusCode.InternalServerError); }, context); return await response.Content.ReadAsStringAsync(cancellationToken); } finally { ResilienceContextPool.Shared.Return(context); } }
如果需要保留CancellationToken的显式传递,也可以使用这个重载:
var response = await pipeline.ExecuteAsync(async (cancellationToken) => { await Task.Delay(100, cancellationToken); return new HttpResponseMessage(HttpStatusCode.InternalServerError); }, context);
这两个重载都会将你传入的context作为策略执行时的上下文,FallbackAction中的args.Context.Properties就能正确获取到你设置的LocationId值。
内容的提问来源于stack exchange,提问作者Alonso Ureña
相关产品推荐
相关产品推荐

