处理并发HTTP请求澄清时

发布于 2025-01-28 02:13:01 字数 1207 浏览 3 评论 0 原文

只需对Pollys超时/重试策略以及处理并发HTTP请求时的工作方式。

从阅读/我自己的理解,将将超时和重试策略应用于每个单独的HTTP请求,因此,如果我们有5个HTTP请求,每个请求都将具有自己的超时和重试策略,因此,从下面的代码中,每个HTTP请求都会超时5秒后重试4次。

    public override void Configure(IFunctionsHostBuilder builder)
    {
        
        var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(5));

        builder.Services
            .AddHttpClient("PointsbetClient")
            .AddPolicyHandler(GetRetryPolicy())
            .AddPolicyHandler(timeout);
    }

    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .Or<TimeoutRejectedException>()
            .WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(
                medianFirstRetryDelay: TimeSpan.FromMilliseconds(500),
                retryCount: 4));
    }

现在说我有一个1000 http请求,我需要对其进行getAsync()调用,以便我可以刮擦他们的数据并出于性能目的,我通过使用等待task.whenall(tasks)并同时进行这些调用; 。由于1000在一次使用 Semaphoreslim 类并将MaxParallelleRequests限制为100的要求时太多

。它仍然适用于每个单独的1000个请求中的每个请求,还是将包含100条请求的1个任务视为一个超时/重试策略?从我的理解中,它仍然可以治疗和应用政策对每个单独的HTTP请求,我一直在搜索,但找不到确认。

Just have a question about Pollys timeout/retry policy and how it works when handling concurrent http requests.

From reading/my own understanding the timeout and retry policy will be applied to each individual http request, so if we have 5 http requests, each of them would have there own timeout and retry policy, so from the code below each http request would timeout after 5 seconds and retry a total of 4 times.

    public override void Configure(IFunctionsHostBuilder builder)
    {
        
        var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(5));

        builder.Services
            .AddHttpClient("PointsbetClient")
            .AddPolicyHandler(GetRetryPolicy())
            .AddPolicyHandler(timeout);
    }

    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .Or<TimeoutRejectedException>()
            .WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(
                medianFirstRetryDelay: TimeSpan.FromMilliseconds(500),
                retryCount: 4));
    }

Now say I have a 1000 http requests to which I need to make GetAsync() calls to so I can scrape their data and for performance purposes, I am making these calls concurrently, by using await Task.WhenAll(tasks);. Since 1000 is way too many requests to hit at the one time I am using SemaphoreSlim class and limiting the MaxParallelRequests to 100.

How would the Polly retry and timeout policy apply now? does it still apply to each of those individual 1000 requests or will it treat 1 task containing 100 requests as a single timeout/retry policy? From my understanding it would still treat and apply policy to each of the individual http requests, I've been searching but can't find confirmation on this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

香草可樂 2025-02-04 02:13:01

简短的答案是,他们被分开对待。


为了了解系统的工作原理,我们必须在引擎盖下看。让我们从 addpolicyhandler 开始我们的旅程。

免责声明为了简洁起见

public static IHttpClientBuilder AddPolicyHandler(this IHttpClientBuilder builder, IAsyncPolicy<HttpResponseMessage> policy)
{
    if (builder == null) throw new ArgumentNullException(nameof(builder));
    if (policy == null) throw new ArgumentNullException(nameof(policy));
    builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy));
    return builder;
}

。 /aspnetcore/blob/A450CB69B5E4549F5515CDB057A687771F56CEFD7/SRC/HTTPCLEINTFACTORY/POLLY/POLLY/SRC/DEPRIANDENCHIN/depporendentynipportion/pollyhtttppclientbuilderextensions.csssentions.css.cs.cs.cs.c.cs.c.cs.c.c.c.cperternertly> ClientbuildErxensions 类,并为 ihttpclientbuilder

如您所见,它对它无能为力,只是注册另一个 httpmessagehandler 进入链条。

现在,让我们看看这个特殊的处理

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    if (request == null) throw new ArgumentNullException(nameof(request));

    // Guarantee the existence of a context for every policy execution, 
    // but only create a new one if needed.
    // This allows later handlers to flow state if desired.
    var cleanUpContext = false;
    var context = request.GetPolicyExecutionContext();
    if (context == null)
    {
        context = new Context();
        request.SetPolicyExecutionContext(context);
        cleanUpContext = true;
    }

    HttpResponseMessage response;
    try
    {
        var policy = _policy ?? SelectPolicy(request);
        response = await policy.ExecuteAsync((c, ct) => SendCoreAsync(request, c, ct), context, cancellationToken).ConfigureAwait(false);
    }
    finally
    {
        if (cleanUpContext)
            request.SetPolicyExecutionContext(null);
    }

    return response;
}

程序看起来像 policyhttpmessagehandler

正如您所看到的那样,

  • 我们要么检索或创建一个新上下文
  • ,要么从注册表中检索策略,要么使用提供的策略
  • ,我们执行了装饰 sendcoreasync 的策略,

因此,魔术发生在哪里。 ? Let's jump to the documentation comment

Polly提供的所有政策均设计为以长寿的方式使用时有效。某些政策,例如
舱壁和断路器保持状态,应在您希望共享舱壁或断路器状态的通话中进行范围。
在自定义方案中使用策略和消息处理程序时,请注意确保正确的寿命。扩展
提供的方法Pollyhttpclientbuilderextensions 旨在为策略分配长寿
并确保当处理程序旋转功能处于活动状态时可以使用它们。

要了解重试与断路器有何不同之处,让我们查看其引擎 s'实现签名

internal static class RetryEngine
{
    internal static TResult Implementation<TResult>(
        Func<Context, CancellationToken, TResult> action,
        Context context,
        CancellationToken cancellationToken,
        ExceptionPredicates shouldRetryExceptionPredicates,
        ResultPredicates<TResult> shouldRetryResultPredicates,
        Action<DelegateResult<TResult>, TimeSpan, int, Context> onRetry,
        int permittedRetryCount = Int32.MaxValue,
        IEnumerable<TimeSpan> sleepDurationsEnumerable = null,
        Func<int, DelegateResult<TResult>, Context, TimeSpan> sleepDurationProvider = null)
    {
    ...
    }
}

“ 174CC53E17BF02DA5E1F2C0D74DFFB4F23A99C0/SRC/POLLY/CRUCIENBREAKER/CRUCIENBREAKER/CRUCIENBREAKERENGINE.CS#l9“ rel =“ nofollow noreferrer”>“ nofollow noreferrer”> croutlerBreakerEngrengine

internal class CircuitBreakerEngine
{
    internal static TResult Implementation<TResult>(
        Func<Context, CancellationToken, TResult> action,
        Context context,
        CancellationToken cancellationToken,
        ExceptionPredicates shouldHandleExceptionPredicates, 
        ResultPredicates<TResult> shouldHandleResultPredicates, 
        ICircuitController<TResult> breakerController)
    {
    ...
    }
}

您在这里要发现的是 基类存储状态信息。其派生类之一是在不同的策略执行之间,

internal readonly ICircuitController<EmptyStruct> _breakerController;
...
CircuitBreakerEngine.Implementation(
    action,
    context,
    cancellationToken,
    ExceptionPredicates,
    ResultPredicates,
    _breakerController);

我希望这会澄清一切。

The short answer is yes they are treated separately.


In order to understand how the system works we have to look under the hood. Let's start our journey at the AddPolicyHandler.

Disclaimer: I've slightly edited the code snippets for the sake of brevity.

public static IHttpClientBuilder AddPolicyHandler(this IHttpClientBuilder builder, IAsyncPolicy<HttpResponseMessage> policy)
{
    if (builder == null) throw new ArgumentNullException(nameof(builder));
    if (policy == null) throw new ArgumentNullException(nameof(policy));
    builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy));
    return builder;
}

This method is defined inside the PollyHttpClientBuilderExtensions class and it provides extension methods for the IHttpClientBuilder.

As you can see it does nothing else just registers yet another HttpMessageHandler into the chain.

Now, let's see how does this special handler look like

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    if (request == null) throw new ArgumentNullException(nameof(request));

    // Guarantee the existence of a context for every policy execution, 
    // but only create a new one if needed.
    // This allows later handlers to flow state if desired.
    var cleanUpContext = false;
    var context = request.GetPolicyExecutionContext();
    if (context == null)
    {
        context = new Context();
        request.SetPolicyExecutionContext(context);
        cleanUpContext = true;
    }

    HttpResponseMessage response;
    try
    {
        var policy = _policy ?? SelectPolicy(request);
        response = await policy.ExecuteAsync((c, ct) => SendCoreAsync(request, c, ct), context, cancellationToken).ConfigureAwait(false);
    }
    finally
    {
        if (cleanUpContext)
            request.SetPolicyExecutionContext(null);
    }

    return response;
}

This method is defined inside the PolicyHttpMessageHandler.

As you can see nothing extraordinary happens here

  • We either retrieve or create a new Context
  • We either retrieve the policy from registry or use the provided one
  • We execute the policy which decorates the SendCoreAsync

So, where does the magic happen? Let's jump to the documentation comment of this class

All policies provided by Polly are designed to be efficient when used in a long-lived way. Certain policies such as the
Bulkhead and Circuit-Breaker maintain state and should be scoped across calls you wish to share the Bulkhead or Circuit-Breaker state.
Take care to ensure the correct lifetimes when using policies and message handlers together in custom scenarios. The extension
methods provided by PollyHttpClientBuilderExtensions are designed to assign a long lifetime to policies
and ensure that they can be used when the handler rotation feature is active.

To understand how does Retry differ from Circuit Breaker lets look at their Engines' Implementation signature

RetryEngine

internal static class RetryEngine
{
    internal static TResult Implementation<TResult>(
        Func<Context, CancellationToken, TResult> action,
        Context context,
        CancellationToken cancellationToken,
        ExceptionPredicates shouldRetryExceptionPredicates,
        ResultPredicates<TResult> shouldRetryResultPredicates,
        Action<DelegateResult<TResult>, TimeSpan, int, Context> onRetry,
        int permittedRetryCount = Int32.MaxValue,
        IEnumerable<TimeSpan> sleepDurationsEnumerable = null,
        Func<int, DelegateResult<TResult>, Context, TimeSpan> sleepDurationProvider = null)
    {
    ...
    }
}

CircuitBreakerEngine

internal class CircuitBreakerEngine
{
    internal static TResult Implementation<TResult>(
        Func<Context, CancellationToken, TResult> action,
        Context context,
        CancellationToken cancellationToken,
        ExceptionPredicates shouldHandleExceptionPredicates, 
        ResultPredicates<TResult> shouldHandleResultPredicates, 
        ICircuitController<TResult> breakerController)
    {
    ...
    }
}

What you have to spot here is the ICircuitController. The CircuitStateController base class stores the state information. One of its derived class is shared between the different policy executions

internal readonly ICircuitController<EmptyStruct> _breakerController;
...
CircuitBreakerEngine.Implementation(
    action,
    context,
    cancellationToken,
    ExceptionPredicates,
    ResultPredicates,
    _breakerController);

I hope this clarify things.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文