Polly 在重试时更改查询字符串
Posted
技术标签:
【中文标题】Polly 在重试时更改查询字符串【英文标题】:Polly to change query string on retry 【发布时间】:2021-03-23 13:03:43 【问题描述】:我正在使用 .NET 5 并希望在重试时使用 Polly 更改请求的查询字符串。
背景 - 我的 IP 地址允许每分钟的请求数量固定。如果超出限制,我会得到一个特定的 4xx 状态代码。在这种情况下,我想添加一个查询字符串参数?key=xxx
来处理峰值。计入 API 密钥的请求成本更高,并且仅在临时达到配额时才应用。
我在不同的地方多次使用命名客户端。
这是 Polly 适合的场景吗?或者从设计的角度来看,在业务逻辑中处理这个是干净的方式吗?然后我需要把这个逻辑包装起来,避免重复自己。
var response = await client.GetStringAsync("https://test.com");
if (!response.IsSuccessStatusCode && response.StatusCode == 4xx)
response = await client.GetStringAsync("https://test.com?key=XXX")
// continue with regular workflow - handling errors or process response
【问题讨论】:
我认为 Polly 不适合这种政策。您也许可以编写自己的处理程序,甚至可能在处理程序中使用 Polly,但我不确定这里是否真的值得使用 Polly。 @dannyyy 我提出的解决方案对您有用吗? 【参考方案1】:GetStringAsync
返回Task<string>
,因此您无法检查响应的StatusCode
。
所以,你需要使用GetAsync
,它返回一个Task<HttpResponseMessage>
。
因为请求 uri 是调用之间唯一发生变化的东西,所以您需要将其作为参数接收:
private static HttpClient client = new HttpClient(); //or use IHttpClientFactory
static async Task<HttpResponseMessage> PerformRequest(string uri)
Console.WriteLine(uri);
return await client.GetAsync(uri);
为了有一个可以通过重试策略执行的无参数操作,我们需要一个地址迭代器和一个围绕 PerformRequest
的包装器:
static IEnumerable<string> GetAddresses()
yield return "https://test.com";
yield return "https://test.com?key=XXX";
...
private static readonly IEnumerator<string> UrlIterator = GetAddresses().GetEnumerator();
static async Task<HttpResponseMessage> GetNewAddressAndPerformRequest()
if (UrlIterator.MoveNext())
return await PerformRequest(UrlIterator.Current);
return null;
每次调用GetNewAddressAndPerformRequest
时,它都会检索下一个备用网址,然后针对该网址执行请求。
剩下的就是重试策略本身:
var retryPolicyForNotSuccessAnd4xx = Policy
.HandleResult<HttpResponseMessage>(response => response != null && !response.IsSuccessStatusCode)
.OrResult(response => response != null && (int)response.StatusCode > 400 && (int)response.StatusCode < 500)
.WaitAndRetryForeverAsync(_ => TimeSpan.FromSeconds(1));
如果GetNewAddressAndPerformRequest
返回null
,因为我们已经用完了备用网址,那么我们将退出重试
如果 statusCode 介于 200 和 299 之间,则退出重试
如果 statusCode 介于 300 和 400 之间或大于 500,则我们退出重试
在所有其他情况下,我们都会执行重试
用法可能如下所示:
var response = await retryPolicyForNotSuccessAnd4xx.ExecuteAsync(async () => await GetNewAddressAndPerformRequest());
if (response == null)
Console.WriteLine("All requests failed");
Environment.Exit(1);
Console.WriteLine(await response.Content.ReadAsStringAsync());
为了完整起见,这里是完整的源代码:
class Program
private static HttpClient client = new HttpClient();
static async Task Main(string[] args)
var retryPolicyForNotSuccessAnd4xx = Policy
.HandleResult<HttpResponseMessage>(response => response != null && !response.IsSuccessStatusCode)
.OrResult(response => response != null && (int)response.StatusCode > 400 && (int)response.StatusCode < 500)
.WaitAndRetryForeverAsync(_ => TimeSpan.FromSeconds(1));
var response = await retryPolicyForNotSuccessAnd4xx.ExecuteAsync(async () => await GetNewAddressAndPerformRequest());
if (response == null)
Console.WriteLine("All requests failed");
Environment.Exit(1);
Console.WriteLine(await response.Content.ReadAsStringAsync());
static IEnumerable<string> GetAddresses()
yield return "https://test.com";
yield return "https://test.com?key=XXX";
private static readonly IEnumerator<string> UrlIterator = GetAddresses().GetEnumerator();
static async Task<HttpResponseMessage> GetNewAddressAndPerformRequest()
=> UrlIterator.MoveNext() ? await PerformRequest(UrlIterator.Current) : null;
static async Task<HttpResponseMessage> PerformRequest(string uri)
Console.WriteLine(uri);
return await client.GetAsync(uri);
【讨论】:
以上是关于Polly 在重试时更改查询字符串的主要内容,如果未能解决你的问题,请参考以下文章