1

我在 .net 核心应用程序中使用重试策略,超过 100 秒后超时。我可能会以某种不正确的方式使用 Poly,或者它是设计使然,只有超时时间增加可能会有所帮助?

这是我使用 Poly 的方式: 启动:

// populate timeouts array from appsettings

            var resilencyOptions = services.BuildServiceProvider().GetRequiredService<IOptions<ResiliencyOptions>>().Value;
            var attempts = resilencyOptions.TimeOutsInSeconds.Count;
            TimeSpan[] timeouts = new TimeSpan[attempts];
            int i = 0;

            foreach (var timeout in resilencyOptions.TimeOutsInSeconds)
            {
                timeouts[i++] = TimeSpan.FromSeconds(timeout);
            }

            // register

            services.AddTransient<LoggingDelegatingHandler>();
            services.AddHttpClient<IMyClient, MyClient>()
                .AddHttpMessageHandler<LoggingDelegatingHandler>()
                .AddPolicyHandler(ResiliencyPolicy.GetRetryPolicy(attempts, timeouts))
                .AddPolicyHandler(ResiliencyPolicy.GetCircuitBreakerPolicy());

图书馆:

/// <summary>
    /// Resiliency policy.
    /// </summary>
    public class ResiliencyPolicy
    {
        /// <summary>
        /// Get a retry policy.
        /// </summary>
        /// <param name="numberofAttempts"> Количество попыток.</param>
        /// <param name="timeOfAttempts"> Массив с таймаутами между попытками, если передается неполный или пустой, попытки делаются в секундах 2^.</param>
        /// <returns></returns>
        public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(int numberofAttempts = 5, TimeSpan[] timeOfAttempts = null)
        {
            //  In case timeOfAttempts is null or its elements count doesnt correspond to number of attempts provided,
            //  we will wait for:
            //  2 ^ 1 = 2 seconds then
            //  2 ^ 2 = 4 seconds then
            //  2 ^ 3 = 8 seconds then
            //  2 ^ 4 = 16 seconds then
            //  2 ^ 5 = 32 seconds

            return HttpPolicyExtensions
                .HandleTransientHttpError()
                .WaitAndRetryAsync(
                    retryCount: numberofAttempts,
                    sleepDurationProvider: retryAttempt =>  ((timeOfAttempts == null) || (timeOfAttempts.Length != numberofAttempts)) ?
                                                            TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) :
                                                            timeOfAttempts[retryAttempt],
                    onRetry: (exception, retryCount, context) =>
                    {
                        Logging.Global.LogError($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, due to: {exception}.");
                    });
        }

        /// <summary>
        /// Get circuit breaker policy.
        /// </summary>
        /// <param name="numberofAttempts">количество попыток</param>
        /// <param name="durationOfBreaksInSeconds">количество секунд (таймаут) между попытками</param>
        /// <returns></returns>
        public static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy(int numberofAttempts = 5, int durationOfBreaksInSeconds = 30)
        {
            return HttpPolicyExtensions
                .HandleTransientHttpError()
                .CircuitBreakerAsync(
                    handledEventsAllowedBeforeBreaking: numberofAttempts,
                    durationOfBreak: TimeSpan.FromSeconds(durationOfBreaksInSeconds)
                );
        }
    }

从自定义 http 客户端调用:

public class MyClient : IMyClient
     {
        private readonly HttpClient _httpClient;
        private readonly ILogger<MyClient> _logger;

        public MyClient(HttpClient httpClient, ILogger<MyClient> logger)
        {
            _httpClient = httpClient;
            _logger = logger;
        }

        public async Task<bool> Notify(string url, Guid id, string orderId, int state, int category, DateTime date, CancellationToken cancellationToken)
        {
            // prepare request

            var request = new
            {
                Id = id,
                OrderId = orderId,
                State = state,
                Category = category,
                Date = date
            };

            var data = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");

            // send request

            _logger.LogInformation("sending request to {url}", url);
            var response = await _httpClient.PostAsync(url, data, cancellationToken);

            // process response

            if (response.IsSuccessStatusCode)
                return true;

            var content = await response.Content.ReadAsStringAsync(cancellationToken);

            response.Content?.Dispose();

            throw new HttpRequestException($"{response.ReasonPhrase}. {content.Replace("\"", "").TrimEnd()}", null, response.StatusCode);
        }
    }

模拟端点可用性的控制器:

    [ApiController]
    [Route("[controller]")]
    public class RabbitController : ControllerBase

    {
        private static int _numAttempts;

        public RabbitController(IBus client)
        {
            _client = client;
        }

        [HttpPost("ProcessTestREST")]
        public IActionResult ProcessTestREST(Object data)
        {
            _numAttempts++;
            if (_numAttempts%4==3)
            {
                return Ok();
            }
            else
            {
                return StatusCode((int)HttpStatusCode.InternalServerError, "Something went wrong");
            }
        }
    }

我收到此错误:“由于配置的 HttpClient.Timeout 已过 100 秒,请求被取消。”

4

1 回答 1

1

检查https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-6.0#dynamically-select-policies

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

builder.Services.AddHttpClient("PollyDynamic")
    .AddPolicyHandler(httpRequestMessage =>
        httpRequestMessage.Method == HttpMethod.Get ? timeoutPolicy : longTimeoutPolicy);

超时策略应在 AddHttpClient 阶段设置,以覆盖官方文档中定义的 100 秒默认值。

您对 polly 相关请求的超时,应该涵盖您的重试策略的最大价值。

如果您想忽略重试,请注意使用自定义客户端,以便超时是默认值。

于 2022-01-09T11:29:00.380 回答