Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Polly断路器策略和带有ASP.NET核心API的HttpClient_C#_Asp.net Core_.net Core_Asp.net Core Webapi_Polly - Fatal编程技术网

C# Polly断路器策略和带有ASP.NET核心API的HttpClient

C# Polly断路器策略和带有ASP.NET核心API的HttpClient,c#,asp.net-core,.net-core,asp.net-core-webapi,polly,C#,Asp.net Core,.net Core,Asp.net Core Webapi,Polly,我在结合HttpClient设置Polly的断路器时遇到问题 具体而言,断路器和HttpClient用于ASP.NET核心Web API控制器,链接如下: 下面是我想要的 重试策略:如果出现暂时性错误,则对每个请求重试3次 电路断路器策略:如果所有请求中出现五个瞬时错误,则生效 问题 尽管重试策略正常工作,但断路器策略不工作 CarController在发生5次来自_httpClient.SendAsync()的异常后仍然接收请求,并且不会暂停30秒(请求由控制器立即处理) 重新分解前已

我在结合
HttpClient
设置Polly的断路器时遇到问题

具体而言,
断路器
HttpClient
用于ASP.NET核心Web API控制器,链接如下:

下面是我想要的

  • 重试策略:如果出现暂时性错误,则对每个请求重试3次

  • 电路断路器策略:如果所有请求中出现五个瞬时错误,则生效

问题

尽管重试策略正常工作,但断路器策略不工作

CarController在发生5次来自_httpClient.SendAsync()的异常后仍然接收请求,并且不会暂停30秒(请求由控制器立即处理)

重新分解前已处理的事件:5

制动持续时间:30秒

我是不是遗漏了什么

配置服务

配置Polly重试和断路器策略,并分别配置自定义HttpClient、
HttpClientService

    public void ConfigureServices(IServiceCollection services)
        {
           services.AddHttpClient();
           services.AddHttpClient<IHttpClientService, HttpClientService>()                                
                        .AddPolicyHandler((service, request) =>
                            HttpPolicyExtensions.HandleTransientHttpError()
                                .WaitAndRetryAsync(3,
                                    retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
                onRetry: (outcome, timespan, retryCount, context) =>
                                {
                                    service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
                                        timespan.TotalMilliseconds, retryCount);
                                }
                            )
)
                        )
                       .AddPolicyHandler((service, request) =>                      
                                HttpPolicyExtensions.HandleTransientHttpError()
                                    //Further external requests are blocked for 30 seconds if five failed attempts occur sequentially.
                                    //Circuit breaker policies are stateful.All calls through this client share the same circuit state.
                                    .CircuitBreakerAsync(5,
                                                 TimeSpan.FromSeconds(30), 
                                                 (result, timeSpan, context)=>
                                                            service.GetService<ILog>().Error("CircuitBreaker onBreak for {delay}ms", timeSpan.TotalMilliseconds),
                                                  context =>
                                                      service.GetService<ILog>().Error("CircuitBreaker onReset")));

         }
HttpClientService

HttpClient在请求之间似乎不是有状态的

断路器不工作:即使在发生来自
\u httpClient.SendAsync()
的五个瞬态错误(例如
HttpRequestException
)后,CarController仍然可以接收请求,并且不会暂停30秒

 [ApiVersion("1")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class CarController : ControllerBase
    {
        private readonly ILog _logger;
        private readonly IHttpClientService _httpClientService;
        private readonly IOptions<Config> _config;

        public CarController(ILog logger, IHttpClientService httpClientService, IOptions<Config> config)
        {
            _logger = logger;
            _httpClientService = httpClientService;
            _config = config;
        }

        [HttpPost]
        public async Task<ActionResult> Post()
        {  

            using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                string body = reader.ReadToEnd();

                    var statusCode = await _httpClientService.PostAsync(
                        "url",
                        new Dictionary<string, string>
                        {
                            {"headerID", "Id"}                           
                        },
                        body);
                    return StatusCode((int)statusCode);               
            }
        }
      }
 public class HttpClientService
{
    private readonly HttpClient _httpClient;
    public HttpClientService(HttpClient client)
    {
        _httpClient = client;
    }

    public async Task<HttpStatusCode> PostAsync(string url, Dictionary<string, string> headers, string body)
    {
        using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
        {
            foreach (var keyValue in headers)
            {
                content.Headers.Add(keyValue.Key, keyValue.Value);
            }

             var request = new HttpRequestMessage(HttpMethod.Post, url)
                                    {
                                        Content = content
                                    };

            var response = await _httpClient.SendAsync(request);

            response.EnsureSuccessStatusCode();
            return response.StatusCode;
        }

    }
公共类HttpClientService
{
私有只读HttpClientu HttpClient;
公共HttpClientService(HttpClient客户端)
{
_httpClient=client;
}
公共异步任务PostAsync(字符串url、字典头、字符串正文)
{
使用(var content=newstringcontent(body,Encoding.UTF8,“application/json”))
{
foreach(标头中的var keyValue)
{
content.Headers.Add(keyValue.Key,keyValue.Value);
}
var request=newhttprequestmessage(HttpMethod.Post,url)
{
内容=内容
};
var response=await\u httpClient.sendsync(请求);
response.EnsureSuccessStatusCode();
返回response.StatusCode;
}
}
ASP.NET核心API 2.2

更新
更新了SetWaitAndRetryPolicy扩展方法以使用IServiceProvider。

断路器策略是,因此需要

发布代码中HttpClientFactory上的重载的使用方式:

.AddPolicyHandler((service, request) => HttpPolicyExtensions.HandleTransientHttpError()
    .CircuitBreakerAsync( /* etc */
是的,因此断路器从来没有时间建立故障状态

重载(和类似的)是。但事实上,发布的代码(编辑:最初发布的代码是)没有使用
服务,请求
输入参数,因此您可以删除
(服务,请求)=>
部分,并使用HttpClientFactory上的重载,该重载采用策略实例:

.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
    .WaitAndRetryAsync(/* etc */))
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
    .CircuitBreakerAsync(/* etc */))
HttpPolicyExtensions.HandletTransientHttpError().CircuitBreakerAsync(/*etc*/)
返回的断路器的单个长期实例将由HttpClient实例使用,HttpClientFactory配置为使用该实例


旁注(特别是对于任何想要使用给定过载的断路器的读者):

可以在HttpClientFactory中将断路器与请求驱动的
policySelector
重载一起使用。只需确保lambda表达式选择单个实例,而不是每次请求都生成一个新实例。例如:

var circuitBreaker = HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */);
services.AddHttpClient<IHttpClientService, HttpClientService>()                                
    .AddPolicyHandler((service, request) => circuitBreaker); // By way of example technique: more typically with this overload, there is some more complex logic to select different policies for different kinds of request.
var-circuitBreaker=HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/*etc*/);
services.AddHttpClient()
.AddPolicyHandler((服务,请求)=>断路器);//通过示例技术:更典型的是,对于这种重载,有一些更复杂的逻辑来为不同类型的请求选择不同的策略。
编辑以回答注释中的问题:该实例不必声明为
static
,即可使其长期存在。它可以在
Startup.ConfigureServices(…)中声明
方法,在使用前立即使用,如上面的代码示例所示。lambda和在HttpClientFactory上配置它将捕获它并使其长寿

断路器
实例应为。如果您将断路器连接到通过HttpClientFactory声明的特定
HttpClient
配置,则通过该
HttpClient
配置实例的所有调用(稍后由DI从HttpClientFactory检索)将共享断路器,从而断开通信n

将断路器与HttpClientFactory一起使用时,这通常意味着您可以在HttpClientFactory上为每个子系统声明一个
HttpClient
配置(键入或命名),您希望对其调用断路器



旁注:还选择了断路器的变型。(仅在额外因素的情况下提及;发布的问题涉及请求之间发生的5个错误,但不是连续发生的。)

我猜问题是因为您调用了两次
AddHttpClient
,但您没有与它共享断路策略(也不是
waitandertryAsync
)。我不太确定您创建
IHttpClientService
的方式。您不应该在注册时为其设置基址吗?还要注意:当您达到异常限制时(在您的情况下为3),状态机将移动到
打开
状态。但这不会阻止您的控制器被调用。控制器本身可以被调用,但
Http
调用将快速失败,出现
brokencircuiiteexception
,并将在30天内保持这种状态