【问题标题】:Polly retry with different urlPolly 使用不同的 url 重试
【发布时间】:2021-11-23 15:58:58
【问题描述】:

我正在尝试使用 polly 创建一个解决方案,我请求其他 api。
我有一个指向同一服务的多个实例的 URL 列表。
我希望当第一个请求失败时,另一个应该自动从我列表中的下一个 url 开始。

这是一个示例,我使用两个静态地址尝试此行为
这个解决方案的问题是,在我开始下一个请求之前,url 不会改变。 我希望每次重试时 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");
            });
    }

【问题讨论】:

    标签: c# asp.net-core polly


    【解决方案1】:

    您应该考虑改用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();
    }
    
    • 诀窍:重试策略包装了一个方法,该方法负责检索下一个 url,然后调用GetData
      • 换句话说,我们需要将迭代过程移动到待包装方法中 (GetNewAddressAndPerformRequest)
    • 我已将 Fallback 政策替换为 Retry,因为我们需要执行(可能)超过 1 个后备操作
    • 我使用 null 表示我们已经用完了后备 URL,但为此使用自定义异常可能是更好的解决方案

    【讨论】:

    • 这可以用 IHttpClientFactory 用 2 个 URL 来完成,而不是很多,比如主要和故障转移?
    • @VergilC。是的,在这种情况下也可以使用完全相同的方法。 .FallbackAsync(fallbackAction: async (ct) =&gt; await GetData(secondaryAddress));retryLikePolicy.ExecuteAsync(async () =&gt; await GetData(primaryAddress))
    • @Peter Csala 重试整个列表的最佳方法是什么(在回退时更改 url),直到一个 url 成功。似乎这个只尝试一个后备。
    • @Nlr 我已经扩展了我的帖子以向您展示如何做到这一点。请检查一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-14
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多