polly 是如何实现超时控制的

一、官方地址

https://www.pollydocs.org/getting-started.html

二、使用

1、引入nuget包

在这里插入图片描述

2、使用:设置数据1秒钟没有回来就是超时
var optionsOnTimeout = new TimeoutStrategyOptions
{
    TimeoutGenerator = static args =>
    {
        // Note: the timeout generator supports asynchronous operations
        return new ValueTask<TimeSpan>(TimeSpan.FromSeconds(1));
    },
    OnTimeout = static args =>
    {
        Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds.");
        return default;
    }
};


var pipeline = new ResiliencePipelineBuilder()
    .AddTimeout(optionsOnTimeout)
    .Build();



CancellationToken cancellationToken= new CancellationToken();

try
{
    HttpResponseMessage httpResponse = await pipeline.ExecuteAsync(
  async ct =>
  {
      var client = new HttpClient();
      var request = new HttpRequestMessage
      {
          Method = HttpMethod.Post,
          RequestUri = new Uri("http://localhost/shop/******/Getdata"),
          Content = new StringContent("{\"Item1\": \"admin\"}")
          {
              Headers =
    {
            ContentType = new MediaTypeHeaderValue("application/json")
    }
          }
      };
      // Execute a delegate that takes a CancellationToken as an input parameter.
      return await client.SendAsync(request, ct);
  },
  cancellationToken);


    if (httpResponse != null)
    {
        httpResponse.EnsureSuccessStatusCode();
        var body = await httpResponse.Content.ReadAsStringAsync();
        Console.WriteLine(body);
    }
}
catch (Exception ex)
{

   Console.WriteLine($"Error: {ex.Message}");
}

三、看如何实现的

源码地址: https://github.com/App-vNext/Polly

当调用 pipeline.ExecuteAsync时会传递两个参数,一个是callback,另一个是控制线程是否取消的cancellationToken。源码如下

ResiliencePipeline.ExecuteAsync
    public async ValueTask<TResult> ExecuteAsync<TResult>(
        Func<CancellationToken, ValueTask<TResult>> callback,
        CancellationToken cancellationToken = default)
    {
        Guard.NotNull(callback);

        var context = GetAsyncContext<TResult>(cancellationToken);

        try
        {
            var outcome = await Component.ExecuteCore(
                static async (context, state) =>
                {
                    TResult data;
                    try
                    {
                        data=  await state(context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext);
                        return Outcome.FromResult(data);
                    }
                    catch (Exception e)
                    {
                        return Outcome.FromException<TResult>(e);
                    }
                   
                },
                context,
                callback).ConfigureAwait(context.ContinueOnCapturedContext);

            return outcome.GetResultOrRethrow();
        }
        finally
        {
            Pool.Return(context);
        }
    }

正在ResiliencePipeline.ExecuteAsync中调用了TimeoutResilienceStrategy.ExecuteCore,此方法传递三个参数,
callback:用来执行pipeline.ExecuteAsync中的callback方法
context:ResiliencePipeline.ExecuteAsync中创建的context
state:注意这里,这个参数是 pipeline.ExecuteAsync中的callback方法

TimeoutResilienceStrategy.ExecuteCore
    protected internal override async ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>(
        Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
        ResilienceContext context,
        TState state)
    {
        var timeout = DefaultTimeout;
        if (TimeoutGenerator is not null)
        {
            timeout = await TimeoutGenerator!(new TimeoutGeneratorArguments(context)).ConfigureAwait(context.ContinueOnCapturedContext);
        }

        if (!TimeoutUtil.ShouldApplyTimeout(timeout))
        {
            // do nothing
            return await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext);
        }

        var previousToken = context.CancellationToken;
        var cancellationSource = _cancellationTokenSourcePool.Get(timeout);
        context.CancellationToken = cancellationSource.Token;

        var registration = CreateRegistration(cancellationSource, previousToken);

        var outcome = await StrategyHelper.ExecuteCallbackSafeAsync(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext);
        var isCancellationRequested = cancellationSource.IsCancellationRequested;

        // execution is finished, clean up
        context.CancellationToken = previousToken;
#pragma warning disable CA1849 // Call async methods when in an async method, OK here as the callback is synchronous
        registration.Dispose();
#pragma warning restore CA1849 // Call async methods when in an async method

        _cancellationTokenSourcePool.Return(cancellationSource);

        // check the outcome
        if (isCancellationRequested && outcome.Exception is OperationCanceledException e && !previousToken.IsCancellationRequested)
        {
            var args = new OnTimeoutArguments(context, timeout);
            _telemetry.Report(new(ResilienceEventSeverity.Error, TimeoutConstants.OnTimeoutEvent), context, args);

            if (OnTimeout is not null)
            {
                await OnTimeout(args).ConfigureAwait(context.ContinueOnCapturedContext);
            }

            var timeoutException = new TimeoutRejectedException(
                $"The operation didn't complete within the allowed timeout of '{timeout}'.",
                timeout,
                e);

            return Outcome.FromException<TResult>(timeoutException.TrySetStackTrace());
        }

        return outcome;
    }

TimeoutResilienceStrategy.ExecuteCore 做了两件事
1、把pipeline.ExecuteAsync控制线程是否取消的cancellationToken替换成设置了CancelAfter(delay)的cancellationToken
2、执行pipeline.ExecuteAsync的callback方法(StrategyHelper.ExecuteCallbackSafeAsync)

StrategyHelper.ExecuteCallbackSafeAsync
    public static ValueTask<Outcome<TResult>> ExecuteCallbackSafeAsync<TResult, TState>(
        Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
        ResilienceContext context,
        TState state)
    {
        if (context.CancellationToken.IsCancellationRequested)
        {
            return new ValueTask<Outcome<TResult>>(Outcome.FromException<TResult>(new OperationCanceledException(context.CancellationToken)));
        }

        try
        {
            var callbackTask = callback(context, state);
            if (callbackTask.IsCompleted)
            {
                return new ValueTask<Outcome<TResult>>(callbackTask.GetResult());
            }

            return AwaitTask(callbackTask, context.ContinueOnCapturedContext);
        }
        catch (Exception e)
        {
            return new ValueTask<Outcome<TResult>>(Outcome.FromException<TResult>(e));
        }

        static async ValueTask<Outcome<T>> AwaitTask<T>(ValueTask<Outcome<T>> task, bool continueOnCapturedContext)
        {
            try
            {
                return await task.ConfigureAwait(continueOnCapturedContext);
            }
            catch (Exception e)
            {
                return Outcome.FromException<T>(e);
            }
        }
    }

StrategyHelper.ExecuteCallbackSafeAsync 先检查了pipeline.ExecuteAsync传递的cancellationToken是否取消,如果超时取消,则抛出异常。
如果没有取消,var callbackTask = callback(context, state) 执行的是Component.ExecuteCore中定义的方法,如果此方法执行失败,抛出异常。


 static async (context, state) =>
                {
                    TResult data;
                    try
                    {
                        data=  await state(context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext);
                        return Outcome.FromResult(data);
                    }
                    catch (Exception e)
                    {
                        return Outcome.FromException<TResult>(e);
                    }
                   
                }
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值