我正在编写一些重新连接逻辑,以定期尝试与远程端点建立连接。本质上,代码如下所示:
public void establishConnection() {
try {
this.connection = newConnection();
} catch (IOException e) {
// connection failed, try again.
try { Thread.sleep(1000); } catch (InterruptedException e) {};
establishConnection();
}
}
我在很多场合用类似上面的代码解决了这个一般问题,但我对结果感到很不满意。是否有设计模式来处理这个问题?
答案 0 :(得分:30)
无耻插件:我已经实现了一些类来允许重试操作。 该库尚未提供,但您可以fork it on github 。
并且存在fork。
它允许使用各种灵活的策略构建Retryer。例如:
Retryer retryer =
RetryerBuilder.newBuilder()
.withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECOND))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.retryIfExceptionOfType(IOException.class)
.build();
然后你可以用Retryer执行一个可调用的(或几个):
retryer.call(new Callable<Void>() {
public Void call() throws IOException {
connection = newConnection();
return null;
}
}
答案 1 :(得分:22)
您可以尝试Idempotent Retry Pattern。
答案 2 :(得分:9)
我正在使用AOP和Java注释。在jcabi-aspects(我是开发人员)中有一个现成的机制:
@RetryOnFailure(attempts = 3, delay = 1, unit = TimeUnit.SECONDS)
public void establishConnection() {
this.connection = newConnection();
}
PS。您也可以从Cactoos尝试RetryScalar
。
答案 3 :(得分:4)
使用Failsafe(此处作者):
RetryPolicy retryPolicy = new RetryPolicy()
.retryOn(IOException.class)
.withMaxRetries(5)
.withDelay(1, TimeUnit.SECONDS);
Failsafe.with(retryPolicy).run(() -> newConnection());
没有注释,没有魔法,不需要成为Spring应用程序等等。只是简单明了。
答案 4 :(得分:3)
一个值得结账的图书馆是Sarge,它会根据定义的计划自动执行重试。
答案 5 :(得分:2)
你可以尝试spring-retry,它有一个干净的界面,而且它很容易使用。
示例:
@Retryable(maxAttempts = 4, backoff = @Backoff(delay = 500))
public void establishConnection() {
this.connection = newConnection();
}
如果发生异常,它将重试(调用)方法 establishConnection()最多4次,退避政策为500毫秒
答案 6 :(得分:2)
我非常喜欢this blog中的这个Java 8代码,并且您的类路径上不需要任何额外的库。
您只需将函数传递给重试类。
@Slf4j
public class RetryCommand<T> {
private int maxRetries;
RetryCommand(int maxRetries)
{
this.maxRetries = maxRetries;
}
// Takes a function and executes it, if fails, passes the function to the retry command
public T run(Supplier<T> function) {
try {
return function.get();
} catch (Exception e) {
log.error("FAILED - Command failed, will be retried " + maxRetries + " times.");
return retry(function);
}
}
private T retry(Supplier<T> function) throws RuntimeException {
int retryCounter = 0;
while (retryCounter < maxRetries) {
try {
return function.get();
} catch (Exception ex) {
retryCounter++;
log.error("FAILED - Command failed on retry " + retryCounter + " of " + maxRetries, ex);
if (retryCounter >= maxRetries) {
log.error("Max retries exceeded.");
break;
}
}
}
throw new RuntimeException("Command failed on all of " + maxRetries + " retries");
}
}
使用它:
new RetryCommand<>(5).run(() -> client.getThatThing(id));
答案 7 :(得分:1)
您还可以创建一个包装器功能,该功能仅对预期的操作进行循环,完成后只需跳出循环即可。
public static void main(String[] args) {
retryMySpecialOperation(7);
}
private static void retryMySpecialOperation(int retries) {
for (int i = 1; i <= retries; i++) {
try {
specialOperation();
break;
}
catch (Exception e) {
System.out.println(String.format("Failed operation. Retry %d", i));
}
}
}
private static void specialOperation() throws Exception {
if ((int) (Math.random()*100) % 2 == 0) {
throw new Exception("Operation failed");
}
System.out.println("Operation successful");
}
答案 8 :(得分:0)
重试没有什么特别之处 - 以此类为例http://www.docjar.com/html/api/org/springframework/jms/listener/DefaultMessageListenerContainer.java.html 正如你所看到的,即使是Spring开发人员仍在为重试编写代码 - 791行...... AFAIK没有这种特殊的模式..
我可以建议处理资源的方法是使用apache commons池库 - 检查http://commons.apache.org/pool/apidocs/org/apache/commons/pool/impl/GenericObjectPool.html并访问http://commons.apache.org/pool
答案 9 :(得分:0)
如果您使用的是Java 8,这可能有所帮助。
import java.util.function.Supplier;
public class Retrier {
public static <T> Object retry(Supplier<T> function, int retryCount) throws Exception {
while (0<retryCount) {
try {
return function.get();
} catch (Exception e) {
retryCount--;
if(retryCount == 0) {
throw e;
}
}
}
return null;
}
public static void main(String[] args) {
try {
retry(()-> {
System.out.println(5/0);
return null;
}, 5);
} catch (Exception e) {
System.out.println("Exception : " + e.getMessage());
}
}
}
谢谢,
Praveen R。
答案 10 :(得分:0)
我正在使用retry4j库。测试代码示例:
public static void main(String[] args) {
Callable<Object> callable = () -> {
doSomething();
return null;
};
RetryConfig config = new RetryConfigBuilder()
.retryOnAnyException()
.withMaxNumberOfTries(3)
.withDelayBetweenTries(5, ChronoUnit.SECONDS)
.withExponentialBackoff()
.build();
new CallExecutorBuilder<>().config(config).build().execute(callable);
}
public static void doSomething() {
System.out.println("Trying to connect");
// some logic
throw new RuntimeException("Disconnected"); // init error
// some logic
}
答案 11 :(得分:0)
这是执行重试的另一种方法。没有库,没有注释,没有额外的实现。导入 java.util.concurrent.TimeUnit;
public static void myTestFunc() {
boolean retry = true;
int maxRetries = 5; //max no. of retries to be made
int retries = 1;
int delayBetweenRetries = 5; // duration between each retry (in seconds)
int wait = 1;
do {
try {
this.connection = newConnection();
break;
}
catch (Exception e) {
wait = retries * delayBetweenRetries;
pause(wait);
retries += 1;
if (retries > maxRetries) {
retry = false;
log.error("Task failed on all of " + maxRetries + " retries");
}
}
} while (retry);
}
public static void pause(int seconds) {
long secondsVal = TimeUnit.MILLISECONDS.toMillis(seconds);
try {
Thread.sleep(secondsVal);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}