有时,我需要在放弃之前将一个操作重试几次。我的代码是:
int retries = 3;
while(true) {
try {
DoSomething();
break; // success!
} catch {
if(--retries == 0) throw;
else Thread.Sleep(1000);
}
}
我想在一个通用的重试函数中重写这个:
TryThreeTimes(DoSomething);
这在c#中可行吗?TryThreeTimes()方法的代码是什么?
重试助手:一个通用的java实现,它包含可返回类型和无效类型的重试。
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RetryHelper {
private static final Logger log = LoggerFactory.getLogger(RetryHelper.class);
private int retryWaitInMS;
private int maxRetries;
public RetryHelper() {
this.retryWaitInMS = 300;
this.maxRetries = 3;
}
public RetryHelper(int maxRetry) {
this.maxRetries = maxRetry;
this.retryWaitInMS = 300;
}
public RetryHelper(int retryWaitInSeconds, int maxRetry) {
this.retryWaitInMS = retryWaitInSeconds;
this.maxRetries = maxRetry;
}
public <T> T retryAndReturn(Supplier<T> supplier) {
try {
return supplier.get();
} catch (Exception var3) {
return this.retrySupplier(supplier);
}
}
public void retry(Runnable runnable) {
try {
runnable.run();
} catch (Exception var3) {
this.retrySupplier(() -> {
runnable.run();
return null;
});
}
}
private <T> T retrySupplier(Supplier<T> supplier) {
log.error("Failed <TASK>, will be retried " + this.maxRetries + " times.");
int retryCounter = 0;
while(retryCounter < this.maxRetries) {
try {
return supplier.get();
} catch (Exception var6) {
++retryCounter;
log.error("<TASK> failed on retry: " + retryCounter + " of " + this.maxRetries + " with error: " + var6.getMessage());
if (retryCounter >= this.maxRetries) {
log.error("Max retries exceeded.");
throw var6;
}
try {
Thread.sleep((long)this.retryWaitInMS);
} catch (InterruptedException var5) {
var5.printStackTrace();
}
}
}
return supplier.get();
}
public int getRetryWaitInMS() {
return this.retryWaitInMS;
}
public int getMaxRetries() {
return this.maxRetries;
}
}
用法:
try {
returnValue = new RetryHelper().retryAndReturn(() -> performSomeTask(args));
//or no return type:
new RetryHelper().retry(() -> mytask(args));
} catch(Exception ex){
log.error(e.getMessage());
throw new CustomException();
}
用波利
https://github.com/App-vNext/Polly-Samples
这是我和波莉一起用的试药
public T Retry<T>(Func<T> action, int retryCount = 0)
{
PolicyResult<T> policyResult = Policy
.Handle<Exception>()
.Retry(retryCount)
.ExecuteAndCapture<T>(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
}
像这样使用它
var result = Retry(() => MyFunction()), 3);
我需要一个支持取消的方法,在此过程中,我添加了返回中间故障的支持。
public static class ThreadUtils
{
public static RetryResult Retry(
Action target,
CancellationToken cancellationToken,
int timeout = 5000,
int retries = 0)
{
CheckRetryParameters(timeout, retries)
var failures = new List<Exception>();
while(!cancellationToken.IsCancellationRequested)
{
try
{
target();
return new RetryResult(failures);
}
catch (Exception ex)
{
failures.Add(ex);
}
if (retries > 0)
{
retries--;
if (retries == 0)
{
throw new AggregateException(
"Retry limit reached, see InnerExceptions for details.",
failures);
}
}
if (cancellationToken.WaitHandle.WaitOne(timeout))
{
break;
}
}
failures.Add(new OperationCancelledException(
"The Retry Operation was cancelled."));
throw new AggregateException("Retry was cancelled.", failures);
}
private static void CheckRetryParameters(int timeout, int retries)
{
if (timeout < 1)
{
throw new ArgumentOutOfRangeException(...
}
if (retries < 0)
{
throw new ArgumentOutOfRangeException(...
}
}
public class RetryResult : IEnumerable<Exception>
{
private readonly IEnumerable<Exception> failureExceptions;
private readonly int failureCount;
protected internal RetryResult(
ICollection<Exception> failureExceptions)
{
this.failureExceptions = failureExceptions;
this.failureCount = failureExceptions.Count;
}
}
public int FailureCount
{
get { return this.failureCount; }
}
public IEnumerator<Exception> GetEnumerator()
{
return this.failureExceptions.GetEnumerator();
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
您可以像这样使用重试功能,重试3次,延迟10秒,但不会取消。
try
{
var result = ThreadUtils.Retry(
SomeAction,
CancellationToken.None,
10000,
3);
// it worked
result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
// oops, 3 retries wasn't enough.
}
或者,除非取消,否则每隔5秒永远重试一次。
try
{
var result = ThreadUtils.Retry(
SomeAction,
someTokenSource.Token);
// it worked
result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
// operation was cancelled before success.
}
正如您可以猜到的那样,在我的源代码中,我重载了Retry函数,以支持我希望使用的不同委托类型。
这可能是个坏主意。首先,这是一句格言的象征:“疯狂的定义是做同一件事两次,每次都期待不同的结果”。其次,这种编码模式本身不能很好地组合。例如:
假设您的网络硬件层在失败时重发一个数据包三次,在两次失败之间等待一秒钟。
现在假设软件层在包失败时重发关于失败的通知三次。
现在假设通知层在通知传递失败时重新激活通知三次。
现在假设错误报告层在通知失败时重新激活通知层三次。
现在假设web服务器在错误失败时重新激活错误报告三次。
现在假设web客户端在从服务器得到错误后重新发送请求三次。
现在假设网络交换机上用来将通知路由到管理员的线路被拔掉。web客户端的用户什么时候最终得到错误消息?我大约12分钟后到。
以免您认为这只是一个愚蠢的例子:我们已经在客户代码中看到了这个错误,尽管比我在这里描述的要严重得多。在特定的客户代码中,错误条件发生和最终报告给用户之间的间隔是几个星期,因为有很多层自动重试并等待。想象一下,如果是十次而不是三次,会发生什么。
通常正确的处理错误的方法是立即报告它,让用户决定怎么做。如果用户想要创建自动重试的策略,让他们在软件抽象的适当级别上创建该策略。