C# 中用于工作流的 Polly 模式?

Polly patterns in C# for a workflow?

我的工作流程是这样的:

两种 API 方法具有相同类型的结果。 我想用 polly 策略来实现这个。

这是我的示例代码:

var retryPolicy = Policy
    .Handle<HttpRequestException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
    .RetryAsync(1, async (exception, retryCount) =>
        await CallAnotherAPI());

var fallbackPolicy = Policy<HttpResponseMessage>
    .Handle<Exception>()
    .FallbackAsync((r, c, ct) => throw r.Exception,
    async (r, c) =>
    {
        Log(r.Message);
    });

var result = await fallbackPolicy
    .WrapAsync(retryPolicy)
    .ExecuteAsync(async () =>
    {
        await CallAPI();
    });

但它不起作用,并且一直执行 fallbackPolicy。怎么写retryPolicy为true不执行fallbackPolicy的代码?

如果我正确理解了您的工作流程,那么您根本不需要重试政策。回退政策就足够了。

所以,假设 CallApiCallAnotherApi 是这样实现的:

private static HttpClient client = new HttpClient();
public static async Task<HttpResponseMessage> CallAPI()
{
    return await client.GetAsync("http://httpstat.us//408");
}

public static async Task<HttpResponseMessage> CallAnotherAPI()
{
    var response = await client.GetAsync("http://httpstat.us//500");
    response.EnsureSuccessStatusCode();
    return response;
}
  • 我已经使用 httpstatus.us 来模拟某些 http 状态代码
  • CallApi 总是会因请求超时而失败
  • CallAnotherApi 将始终抛出 HttpRequestException 因为 Ensure 方法调用

现在让我们看看策略定义和用法:

public static async Task Main(string[] args)
{
    var fallbackPolicy = Policy<HttpResponseMessage>
    .HandleResult(msg => msg.StatusCode == HttpStatusCode.RequestTimeout)
    .FallbackAsync(async (_) => await CallAnotherAPI());

    HttpResponseMessage result;
    try
    {
        result = await fallbackPolicy
            .ExecuteAsync(async () =>
            {
                return await CallAPI();
            });
    }
    catch (Exception ex) {
        Console.WriteLine(ex.Message); //TODO: replace with logging
        throw;
    }

    Console.WriteLine(result.StatusCode);
}
  • 仅当响应的状态代码为 408 时才应触发回退策略
  • 如果 CallApiCallAnotherApi 抛出 ,
  • ExecuteAsync 将抛出异常

让我们逐一看看不同的场景

CallApi 成功

public static async Task<HttpResponseMessage> CallAPI()
{
    return await client.GetAsync("http://httpstat.us//200");
}

输出

OK

CallApi 失败

public static async Task<HttpResponseMessage> CallAPI()
{
    var response = await client.GetAsync("http://httpstat.us//500");
    response.EnsureSuccessStatusCode();
    return response;
}

输出

Response status code does not indicate success: 500 (Internal Server Error).

然后应用程序因为throw;

而崩溃

CallApi 超时,CallAnotherApi 成功

public static async Task<HttpResponseMessage> CallAPI()
{
    return await client.GetAsync("http://httpstat.us//408");
}

public static async Task<HttpResponseMessage> CallAnotherAPI()
{
    var response = await client.GetAsync("http://httpstat.us//200");
    response.EnsureSuccessStatusCode();
    return response;
}

输出

OK

CallApi 超时和 CallAnotherApi 失败

public static async Task<HttpResponseMessage> CallAPI()
{
    return await client.GetAsync("http://httpstat.us//408");
}

public static async Task<HttpResponseMessage> CallAnotherAPI()
{
    var response = await client.GetAsync("http://httpstat.us//500");
    response.EnsureSuccessStatusCode();
    return response;
}

输出

Response status code does not indicate success: 500 (Internal Server Error).

然后应用程序因为throw;

而崩溃

在这种情况下,polly 回退就足够了。像这样....

var fallbackPolicy = Policy<HttpResponseMessage>
.Handle<Exception>()
.FallbackAsync((r, c, ct) => throw r.Exception,
async (r, c) =>
{
    Log(r.Message);
});