筆記Polly的各種用法
nuget package
dotne add package Microsoft.Extensions.Http.Polly
Retry策略
1.Startup
- 針對IHttpClientBuilder
IHttpClientBuilder httpClientBuilder = services.AddHttpClient("Test"); IHttpClientBuilder clientBuilder = services.AddHttpClient<MyService>();
- 重試三次,分別在錯誤後等待1、5、10秒(總共會發出4次的request)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient("Test")
.AddTransientHttpErrorPolicy(builder =>
{
return builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
});
});
}
2.Controller
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var httpClient = _clientFactory.CreateClient("Test");
return await httpClient.GetAsync("http://tasdasdxcsswweest.com");
}
3.執行結果
4.Statup的寫法也可以改用這種,自定義規則,仍然有相同的執行結果
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var policySelector = Policy.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(TestPredicate)
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5),
});
services.AddHttpClient("Test")
.AddPolicyHandler(policySelector);
}
Timeout
1.Startup
var policySelector = Policy.TimeoutAsync<HttpResponseMessage>(1);
services.AddHttpClient("Test")
.AddPolicyHandler(policySelector);
2.Polly的Timeout是包含了期間Retry以及等待的時間跟HttpClient的Timeout稍有不同
動態選擇策略
1.Startup
- 若使用HttpGet,則使用重試策略,否則不做任何事
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var policySelector = Policy.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => true)
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5),
});
var noOpPolicy = Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();
services.AddHttpClient("Test")
.AddPolicyHandler(message => message.Method == HttpMethod.Get
? policySelector
: noOpPolicy);
}
註冊表
1.Startup
- 事前註冊好各種策略
- 後續加入HttpClient,並根據註冊的名稱指定策略
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var keyValuePairs = new PolicyRegistry()
{
{
"Test", HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5),
})
},
{
"No", HttpPolicyExtensions.HandleTransientHttpError()
.CircuitBreakerAsync(10,
TimeSpan.FromSeconds(10))
},
};
services.AddPolicyRegistry(keyValuePairs);
services.AddHttpClient("Test")
.AddPolicyHandlerFromRegistry("Test")
.AddPolicyHandlerFromRegistry("No");
}
斷路器
1.Startup
- 當發生了一次錯誤,後續10秒內的請求都會中斷,直接返回exception
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddPolicyRegistry(new PolicyRegistry()
{
{
"No", HttpPolicyExtensions.HandleTransientHttpError()
.CircuitBreakerAsync(1,
TimeSpan.FromSeconds(10))
},
});
services.AddHttpClient("Test")
.AddPolicyHandlerFromRegistry("No");
}
隔板隔離策略
1.Startup
- 第一個參數,指定每次允許的請求數量
- 第二個參數,最大的排隊數量
- 若超過第一及第二的設定數量,要做的事情
- 被隔板隔離會拿到BulkheadRejectedException
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddPolicyRegistry(new PolicyRegistry()
{
{
"Test", Policy.BulkheadAsync<HttpResponseMessage>(1,
2,
context =>
{
return Task.CompletedTask;
})
},
});
services.AddHttpClient("Test")
.AddPolicyHandlerFromRegistry("Test");
}
2.Controller
[HttpGet]
public HttpResponseMessage Get()
{
Parallel.For(1, 10, i =>
{
var httpClient = _clientFactory.CreateClient("Test");
httpClient.PostAsync("http://tasdasdxcsswweest.com", null)
.GetAwaiter()
.GetResult();
});
return null;
}
3.Result
Cache
1.Controller
- 指定快取的Key
- 需要自行呼叫EnsureSuccessStatusCode檢查是否為HttpStatusCode.OK
- 若要在這層做快取,建議先思考是否適合
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var httpClient = _clientFactory.CreateClient("Test");
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://www.google.com");
httpRequestMessage.SetPolicyExecutionContext(new Polly.Context("8888"));
return await httpClient.SendAsync(httpRequestMessage);
}
2.Polly.Context有提供一個Dictionary來傳遞資料,若有在Startup各個策略中的Hook加上行為,可以透過這個Dictionary取得資料
// Controller
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var context = new Polly.Context("8888");
context["TestValue"] = "test";
var httpClient = _clientFactory.CreateClient("Test");
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://www.gooasdasdasdgle.com");
httpRequestMessage.SetPolicyExecutionContext(context);
return await httpClient.SendAsync(httpRequestMessage);
}
// Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
},
onRetryAsync: async (outcome, timespan, retryCount, context) =>
{
Console.WriteLine(context["TestValue"]);
});
services.AddHttpClient("Test")
.AddPolicyHandler(retryPolicy);
}
3.若需要在Hook中取得DI容器內的Service
// Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient("Test")
.AddPolicyHandler((provider, message) =>
HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
},
(outcome, timespan, retryCount, context) =>
{
var logger = provider.GetService<ILogger<HttpClient>>();
logger.LogError("Error");
}));
}
早早就聽過Polly這套library,不過一直沒有機會用到它
這次挑了點時間把玩了一輪,筆記一下各個策略的用法
Sample Code https://github.com/ianChen806/PollySample