确保 Polly 策略至少运行一次
Ensure Polly Policy Runs at Least Once
所以,我正在编写一些使用 Polly 获取锁的重试逻辑。总超时值将由 API 调用者提供。我知道我可以在整体超时中包装策略。但是,如果提供的超时值太低,有什么方法可以确保该策略至少执行一次?
显然我可以在执行策略之前单独调用委托,但我只是想知道是否有一种方法可以在 Polly 中表达这个要求。
var result = Policy.Timeout(timeoutFromApiCaller)
.Wrap(Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500))
.Execute(() => this.TryEnterLock());
如果 timeoutFromApiCaller
是 1 tick
并且很可能需要比这更长的时间才能达到超时策略,那么委托将不会被调用(该策略将超时并抛出 TimeoutRejectedException
).
我希望发生的事情可以表示为:
var result = this.TryEnterLock();
if (!result)
{
result = Policy.Timeout(timeoutFromApiCaller)
.Wrap(Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500))
.Execute(() => this.TryEnterLock());
}
不过要是能用纯Polly的方式表达就好了...
老实说,我不明白 1 tick 是什么意思?是纳秒还是大于纳秒?您的全局超时应大于本地超时。
但据我所知,您未指定本地。 TryEnterLock
应该收到一个 TimeSpan,以免无限期地阻塞调用者。如果您查看内置的同步原语,它们中的大多数都提供这样的功能:Monitor.TryEnter, SpinLock.TryEnter, WaitHandle.WaitOne,等等
所以,总结一下:
var timeoutPolicy = Policy.Timeout(TimeSpan.FromMilliseconds(1000));
var retryPolicy = Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500));
var resilientStrategy = Policy.Wrap(timeoutPolicy, retryPolicy);
var result = resilientStrategy.Execute(() => this.TryEnterLock(TimeSpan.FromMilliseconds(100)));
超时和延迟值应根据您的业务需要进行调整。我强烈建议您记录全局超时 (onTimeout
/ onTimeoutAsync
) 触发和重试 (onRetry
/ onRetryAsync
) 的时间,以便能够微调/校准这些值。
编辑:基于此 post
的评论
事实证明,timeoutFromApiCaller
无法控制,因此它可以任意小。 (在给定的示例中,它只是几纳秒,目的是强调这个问题。)因此,为了至少有一个调用保证,我们必须使用 Fallback policy.
我们应该将其称为最后一个操作,而不是预先手动调用策略外的TryEnterLock
,以满足要求。因为策略使用升级,这就是为什么每当 inner 失败时它就会将问题委托给下一个 outer 策略。
因此,如果提供的超时时间太短以至于操作要到那个时间才能完成,那么它将抛出 TimeoutRejectedException
。有了回退,我们可以处理这个问题,并且可以再次 执行操作,但现在没有任何超时限制。这将为我们提供所需的至少一项保证。
var atLeastOnce = Policy.Handle<TimeoutRejectedException>
.Fallback((ct) => this.TryEnterLock());
var globalTimeout = Policy.Timeout(TimeSpan.FromMilliseconds(1000));
var foreverRetry = Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500));
var resilientStrategy = Policy.Wrap(atLeastOnce, globalTimeout, foreverRetry);
var result = resilientStrategy.Execute(() => this.TryEnterLock());
所以,我正在编写一些使用 Polly 获取锁的重试逻辑。总超时值将由 API 调用者提供。我知道我可以在整体超时中包装策略。但是,如果提供的超时值太低,有什么方法可以确保该策略至少执行一次?
显然我可以在执行策略之前单独调用委托,但我只是想知道是否有一种方法可以在 Polly 中表达这个要求。
var result = Policy.Timeout(timeoutFromApiCaller)
.Wrap(Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500))
.Execute(() => this.TryEnterLock());
如果 timeoutFromApiCaller
是 1 tick
并且很可能需要比这更长的时间才能达到超时策略,那么委托将不会被调用(该策略将超时并抛出 TimeoutRejectedException
).
我希望发生的事情可以表示为:
var result = this.TryEnterLock();
if (!result)
{
result = Policy.Timeout(timeoutFromApiCaller)
.Wrap(Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500))
.Execute(() => this.TryEnterLock());
}
不过要是能用纯Polly的方式表达就好了...
老实说,我不明白 1 tick 是什么意思?是纳秒还是大于纳秒?您的全局超时应大于本地超时。
但据我所知,您未指定本地。 TryEnterLock
应该收到一个 TimeSpan,以免无限期地阻塞调用者。如果您查看内置的同步原语,它们中的大多数都提供这样的功能:Monitor.TryEnter, SpinLock.TryEnter, WaitHandle.WaitOne,等等
所以,总结一下:
var timeoutPolicy = Policy.Timeout(TimeSpan.FromMilliseconds(1000));
var retryPolicy = Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500));
var resilientStrategy = Policy.Wrap(timeoutPolicy, retryPolicy);
var result = resilientStrategy.Execute(() => this.TryEnterLock(TimeSpan.FromMilliseconds(100)));
超时和延迟值应根据您的业务需要进行调整。我强烈建议您记录全局超时 (onTimeout
/ onTimeoutAsync
) 触发和重试 (onRetry
/ onRetryAsync
) 的时间,以便能够微调/校准这些值。
编辑:基于此 post
的评论事实证明,timeoutFromApiCaller
无法控制,因此它可以任意小。 (在给定的示例中,它只是几纳秒,目的是强调这个问题。)因此,为了至少有一个调用保证,我们必须使用 Fallback policy.
我们应该将其称为最后一个操作,而不是预先手动调用策略外的TryEnterLock
,以满足要求。因为策略使用升级,这就是为什么每当 inner 失败时它就会将问题委托给下一个 outer 策略。
因此,如果提供的超时时间太短以至于操作要到那个时间才能完成,那么它将抛出 TimeoutRejectedException
。有了回退,我们可以处理这个问题,并且可以再次 执行操作,但现在没有任何超时限制。这将为我们提供所需的至少一项保证。
var atLeastOnce = Policy.Handle<TimeoutRejectedException>
.Fallback((ct) => this.TryEnterLock());
var globalTimeout = Policy.Timeout(TimeSpan.FromMilliseconds(1000));
var foreverRetry = Policy.HandleResult(false)
.WaitAndRetryForever(_ => TimeSpan.FromMilliseconds(500));
var resilientStrategy = Policy.Wrap(atLeastOnce, globalTimeout, foreverRetry);
var result = resilientStrategy.Execute(() => this.TryEnterLock());