我正在尝试使用 polly 创建一个解决方案,其中我请求其他 api。
我有同一服务的多个实例的 URL 列表。
我希望当第一个请求失败时,另一个请求应该自动从我的列表中的下一个网址开始。
这是一个示例,我使用两个静态地址尝试此行为
此解决方案的问题是,在我开始下一个请求之前,网址不会更改。
我希望每次重试时网址都会改变
public static void ConfigureUserServiceClient(this IServiceCollection services)
{
_userServiceUri = new Uri("https://localhost:5001");
services.AddHttpClient("someService", client =>
{
client.BaseAddress = _userServiceUri;
client.DefaultRequestHeaders.Add("Accept", "application/json");
}).AddPolicyHandler(retryPolicy());
}
private static IAsyncPolicy<HttpResponseMessage> retryPolicy()
{
return Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.RequestTimeout)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(retryAttempt),
onRetry: (result, span, ctx) =>
{
_userServiceUri = new Uri("https://localhost:5002");
});
}
您应该考虑使用Fallback政策代替。
像这样:
private static HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
var addressIterator = GetUrls().GetEnumerator();
var retryLikePolicy = Policy<string>
.Handle<HttpRequestException>()
.FallbackAsync(fallbackAction: async (ct) =>
{
if (addressIterator.MoveNext())
return await GetData(addressIterator.Current);
return null;
});
addressIterator.MoveNext();
var data = await retryLikePolicy.ExecuteAsync(
async () => await GetData(addressIterator.Current));
Console.WriteLine("End");
}
static async Task<string> GetData(string uri)
{
Console.WriteLine(uri);
var response = await client.GetAsync(uri);
return await response.Content.ReadAsStringAsync();
}
static IEnumerable<string> GetUrls()
{
yield return "http://localhost:5500/index.html";
yield return "http://localhost:5600/index.html";
yield return "http://localhost:5700/index.html";
}
请注意,此代码仅用于演示。
更新#1: 多重后备
如果您有多个后备网址,那么您可以像这样更改上述代码:
private static HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
var retryInCaseOfHRE = Policy
.Handle<HttpRequestException>()
.WaitAndRetryForeverAsync(_ => TimeSpan.FromSeconds(1));
var response = await retryInCaseOfHRE.ExecuteAsync(
async () => await GetNewAddressAndPerformRequest());
if (response == null)
{
Console.WriteLine("All requests failed");
Environment.Exit(1);
}
Console.WriteLine("End");
}
static IEnumerable<string> GetAddresses()
{
yield return "http://localhost:5500/index.html";
yield return "http://localhost:5600/index.html";
yield return "http://localhost:5700/index.html";
yield return "http://localhost:5800/index.html";
}
static readonly IEnumerator<string> AddressIterator = GetAddresses().GetEnumerator();
static async Task<string> GetNewAddressAndPerformRequest()
=> AddressIterator.MoveNext() ? await GetData(AddressIterator.Current) : null;
static async Task<string> GetData(string uri)
{
Console.WriteLine(uri);
var response = await client.GetAsync(uri);
return await response.Content.ReadAsStringAsync();
}
- The trick: the retry policy wraps a method which is responsible to retrieve the next url and then call the
GetData
- 换句话说,我们需要将迭代过程移至要包装的方法中(
GetNewAddressAndPerformRequest
)
- 我已经更换了
Fallback
政策Retry
因为我们需要(可能)执行超过 1 个后备操作
- 我用过
null
表明我们已经用完了后备网址,但为此使用自定义异常可能是更好的解决方案
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)