68

我正在编写一些重新连接逻辑,以定期尝试建立与已关闭的远程端点的连接。本质上,代码如下所示:

public void establishConnection() {
    try {
        this.connection = newConnection();
    } catch (IOException e) {
        // connection failed, try again.
        try { Thread.sleep(1000); } catch (InterruptedException e) {};

        establishConnection();
    }
}

我已经多次使用与上述类似的代码解决了这个普遍问题,但我对结果很不满意。是否有专门用于处理此问题的设计模式?

4

12 回答 12

31

无耻插件:我已经实现了一些允许重试操作的类。该库尚未提供,但您可以在 github 上 fork. 并且存在一个叉子

它允许构建具有各种灵活策略的重试器。例如:

Retryer retryer = 
    RetryerBuilder.newBuilder()
                  .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECOND))
                  .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                  .retryIfExceptionOfType(IOException.class)
                  .build();

然后您可以使用重试器执行一个可调用的(或多个):

retryer.call(new Callable<Void>() {
    public Void call() throws IOException {
        connection = newConnection();
        return null;
    }
}
于 2012-07-27T17:45:33.693 回答
29

您可以尝试Idempotent Retry Pattern

在此处输入图像描述

于 2012-07-27T17:26:36.607 回答
15

我真的很喜欢这个博客中的 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));
于 2018-05-25T17:03:31.487 回答
12

使用故障安全(作者在这里):

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(IOException.class)
  .withMaxRetries(5)
  .withDelay(1, TimeUnit.SECONDS);

Failsafe.with(retryPolicy).run(() -> newConnection());

没有注释,没有魔法,不需要是 Spring 应用程序等。简单明了。

于 2016-07-26T23:01:43.800 回答
11

我正在使用 AOP 和 Java 注释。jcabi-aspects中有一个现成的机制(我是开发者):

@RetryOnFailure(attempts = 3, delay = 1, unit = TimeUnit.SECONDS)
public void establishConnection() {
  this.connection = newConnection();
}

附言。您也可以RetryScalarCactoos尝试。

于 2013-02-03T08:39:14.860 回答
5

你可以试试spring-retry,它有一个干净的界面,而且很容易使用。

例子:

 @Retryable(maxAttempts = 4, backoff = @Backoff(delay = 500))
 public void establishConnection() {
    this.connection = newConnection();
 } 

如果出现异常,它将重试(调用)方法建立连接( )最多 4 次,退避策略为 500 毫秒

于 2016-02-19T22:14:27.013 回答
4

Sarge是一个值得一试的库,它会根据定义的计划自动执行重试。

于 2012-08-08T05:10:59.107 回答
3

您还可以创建一个包装器函数,该函数仅对预期操作进行循环,并且何时完成就跳出循环。

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");
}
于 2019-02-27T16:07:09.177 回答
1

如果您使用的是 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());
    }
}
}

谢谢,

普拉文·R。

于 2018-04-12T17:52:09.533 回答
0

重试没有什么特别的 - 以这个类为例http://www.docjar.com/html/api/org/springframework/jms/listener/DefaultMessageListenerContainer.java.html 正如你所看到的,即使是spring开发人员仍在写作重试代码 - 第 791 行......没有这样的特殊模式 AFAIK..

我可以建议处理资源的是使用 apache commons pool library - 检查这个http://commons.apache.org/pool/apidocs/org/apache/commons/pool/impl/GenericObjectPool.html并访问http:// /commons.apache.org/pool

于 2012-07-27T17:29:29.380 回答
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
}
于 2019-12-06T09:38:21.763 回答
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();
    }
}

}

于 2021-05-09T19:45:08.523 回答