一、官方地址
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);
}
}