给定以下f#片段
//User Code
.. code that can throw exceptions
"Success"
P1政策Policy
.Handle<CosmosException>(fun cx -> cx.StatusCode = HttpStatusCode.TooManyRequests)
.WaitAndRetryForever((fun _ cx _ -> (cx :?> CosmosException).RetryAfter.Value), (fun _ _ _ _ -> ()))
P2政策Policy<string>
.Handle<Exception>()
.Fallback("Failure")
P3政策Policy<string>
.Handle<Exception>()
.Fallback(fun ex -> ex.Message)
问题1 -如何结合P1和P2?
将P1和P2组合在策略p中,使:
- 如果用户代码成功,则"成功";返回给调用者
- 如果用户代码抛出CosmosException, P使用返回的RetryAfter时间跨度永远重试
- 如果用户代码抛出任何其他异常,则"失败";返回给调用者
问题# 2 -如何写P3?
在构造回退返回值 时,似乎没有允许访问处理过的异常来使用它的回退重载。Final作用域是组合P1和P3来获得一个策略PFinal,使得:
- 如果用户代码成功,则"成功";返回给调用者
- 如果用户代码抛出CosmosException, PFinal使用返回的RetryAfter时间span永远重试
- 如果用户代码抛出任何其他异常,则将处理过的异常消息返回给调用者
问题1的答案
为了能够链接策略,您需要将它们定义为兼容策略。你的p2
返回一个string
,而你的p1
什么也不返回。因此,您需要更改p1
以返回string
。然后你可以使用Policy.Wrap
来定义链接,升级。
我不是一个f#开发人员,所以我将在c#中提出解决方案。但是在两种语言中是一样的:
var p1 = Policy<string>
.Handle<CosmosException>(ex => ex.StatusCode == HttpStatusCode.TooManyRequests)
.WaitAndRetryForever(sleepDurationProvider: (_, dr, __) => ((CosmosException)dr.Exception).RetryAfter.Value,
onRetry: (_, __, ___) => { });
var p2 = Policy<string>
.Handle<Exception>()
.Fallback("Failure");
var p = Policy.Wrap(p1, p2);
- 请注意,我们必须在
p1
中将 - 还要注意
sleepDurationProvider
不会接收Exception
作为参数- 相反,它将收到一个
DelegateResult<string>
,它有两个互斥的属性:Exception
和Result
- 相反,它将收到一个
Policy
更改为Policy<string>
问题2的答案
fallbackAction
委托接收DelegateResult<string>
作为参数
var p2 = Policy<string>
.Handle<Exception>()
.Fallback(fallbackAction: (dr, _, __) => dr.Exception.Message,
onFallback: (_, __) => { });
Update # 1:提供清晰度
将p1
定义从Policy
更改为Policy<string>
还有另一个含义:您要装饰的代码应该返回一个字符串(即"Success")
修改前:
//user code
//to be decorated code which might throw exception
return "Success";
修改后:
//user code
//The decorated code either return "Success" or throws CosmosException
return combinedPolicy.Execute(...
更新# 2修复排序
我建议链接p1
和p2
与Policy.Wrap
。很不幸,我给你看错了订单Policy.Wrap(p1, p2)
。正确的是Policy.Wrap(p2, p1)
,因为最右边的参数是最内部的,最左边的参数是最外部的。所以,在你的例子中,重试是内部的,而回退是外部的。
给您带来的不便我深表歉意。