11

我的 Java 应用程序在远程调用失败时需要重试逻辑。这些远程调用是:

  • 分散在应用程序中
  • 属于不同的远程服务类。

此外,重试逻辑可能具有不同的重试间隔和不同的重试尝试。

我需要一个通用的 retry() 实现,它可以根据调用的位置进行适当的方法调用。下面是我正在寻找的简单代码说明。我知道我们可以尝试使用 java 反射来做到这一点,但是,是否有一个框架或开源可用的地方是可读的?

try {
 ClassA objA = remoteServiceA.call(paramA1, paramA2, ...);
} catch (Exception e){
 ClassA objA = (ClassA)retry(remoteService, listOfParams, ..); // generic method call
}
..

try {
 ClassB objB = remoteServiceB.call(paramB1, paramB2, ...);
} catch (Exception e){
 ClassA objB = (ClassB)retry(remoteService, listOfParams, ..); // generic method call
}
4

8 回答 8

12

如前所述,您应该使用 AOP 和 Java 注释。我会推荐一个来自jcabi-aspects的已读机制(我是开发人员):

@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
  return url.openConnection().getContent();
}

另请阅读这篇博文:http ://www.yegor256.com/2014/08/15/retry-java-method-on-exception.html

更新:RetryFuncCactoos检查。

于 2013-02-03T08:30:30.560 回答
1

这是一个可以使用(或一般的Spring 文档中的8.2.7 示例Java 开发人员应该学习和使用 AspectJ 的 5 个原因

基本上,一个方面会拦截对给定方法的所有调用(使用注释、命名约定等指定)并重试。

于 2012-06-06T20:35:39.290 回答
1

假设您有一个方法,需要每 500 毫秒重试一次,最多 5 次。 当前班级

public class RemoteCaller{
    Service serviceCaller;
    public void remoteCall(String message) {
                serviceCaller.updateDetails( this.message);
                return null;
    }
}

修改方法:

public class RetriableHelper<T> implements Callable<T> {

    private Callable<T> task;

    private int numberOfRetries;

    private int numberOfTriesLeft;

    private long timeToWait;


    public RetriableHelper(int numberOfRetries, long timeToWait, Callable<T> task) {
        this.numberOfRetries = numberOfRetries;
        numberOfTriesLeft = numberOfRetries;
        this.timeToWait = timeToWait;
        this.task = task;
    }

    public T call() throws Exception {
        while (true) {
            try {
                return task.call();
            } catch (InterruptedException e) {
                throw e;
            } catch (CancellationException e) {
                throw e;
            } catch (Exception e) {
                numberOfTriesLeft--;
                if (numberOfTriesLeft == 0) {
                    throw e; 
                }
                Thread.sleep(timeToWait);
            }
        }
    }
}


Backend system/remote call class:

public class RemoteCaller{

    Service serviceCaller;

    public void remoteCall(String message) {

        class RemoteCallable implements Callable<Void> {
            String message;
            public RemoteCallable( String message)
            {
                this.message = message;
            }
            public Void call() throws Exception{
                serviceCaller.updateDetails( this.message);
                return null;
            }
        }


        RetriableHelper<Void> retriableHelper = new RetriableHelper<Void>(5, 500, new RemoteCallable( message));
        try {
            retriableHelper.call();
        } catch (Exception e) {
            throw e;
        } 
     }
}
于 2015-06-30T14:51:36.400 回答
1

在此处输入链接描述 Spring 有一个重试注释,用于服务目的

Step 1: Add following dependency to your POM

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>


Step 2: Enabling Spring Retry

To enable Spring Retry in an application, we need to add the @EnableRetry annotation to our @Configuration class:

Ex:

@Configuration
@EnableRetry
public class AppConfig { ... }


Step 3: To add retry functionality to methods, @Retryable can be used:

Ex: 

@Service
public interface MyService {
    @Retryable(
      value = { SQLException.class }, 
      maxAttempts = 2,
      backoff = @Backoff(delay = 5000))
    void retryService(String sql) throws SQLException;
    ...
}


Step 4.The @Recover annotation is used to define a separate recovery method when a @Retryable method fails with a specified exception:

Ex: 

@Service
public interface MyService {
    ...
    @Recover
    void recover(SQLException e, String sql);
}


See Url for more details : http://www.baeldung.com/spring-retry
于 2018-05-23T17:58:11.220 回答
0

您从哪里获得服务?使用工厂代理您从原始工厂获得的服务。然后代理可以透明地实现重试。请参阅反射中的 java Proxy/ProxyGenerators。

于 2012-06-06T20:31:38.847 回答
0

如果您使用的是 spring ,那么最好使用 Aspects。
否则,以下示例解决方案可以工作:

public class Test
{
    public static void main(String[] args) throws Exception
    {
        Test test = new Test();
        test.toRunFirst("Hello! This is normal invocation");
        runWithRetry(test, "toRunFirst", "Hello! This is First, called with retry");
        runWithRetry(test, "toRunSecond", "Hello! This is Second, called with retry");
    }


    public void toRunFirst(String s) {
        System.out.println(s);
    }
    public void toRunSecond(String s) {
        System.out.println(s);
    }

    public static Object runWithRetry(Object obj, String methodName, Object... args) throws Exception
    {
        Class<?>[] paramClass = new Class<?>[args.length];
        for(int i=0; i< args.length; i++) {
            paramClass[i] = args[i].getClass();
        }
        Method method = obj.getClass().getDeclaredMethod(methodName, paramClass);

        int retryCount = 2;

        for(int i=0; i< retryCount; i++) {
            try {
                return  method.invoke(obj, args);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
于 2019-12-09T14:14:00.070 回答
0

我没有找到我需要的东西,所以有我的。主要特点是它会在达到 maxRetries 时抛出您需要的 Exception 类型,以便您可以在调用中捕获它。

import org.apache.log4j.Logger;

public class TaskUtils {

    public static <E extends Throwable> void retry(int maxRetries, Task<E> task) throws E {
        retry(maxRetries, 0, null, task);
    }

    public static <E extends Throwable> void retry(int maxRetries, long waitTimeMs, Logger logger, Task<E> task) throws E {
        while (maxRetries > 0) {
            maxRetries--;
            try {
                task.run();
            } catch (Exception e) {
                if (maxRetries == 0) {
                    try {
                        throw e;
                    } catch (Exception ignored) { // can't happen but just in case we wrap it in
                        throw new RuntimeException(e);
                    }
                }

                if (logger != null)
                    logger.warn("Attempt " + maxRetries + " failed", e);
                try {
                    Thread.sleep(waitTimeMs);
                } catch (InterruptedException ignored) {
                }
            }
        }
    }

    public interface Task<E extends Throwable> {
        void run() throws E;
    }
}

用法 :

TaskUtils.retry(3, 500, LOGGER, () -> stmClickhouse.execute(
                        "ALTER TABLE `" + database + "`.`" + table.getName() + "` ON CLUSTER " + clusterName + allColumnsSql
                ));
于 2021-07-27T13:33:45.337 回答
0

将其添加到 pom.xml

<dependency>
    <groupId>org.deking.utils</groupId>
    <artifactId>retry</artifactId>
    <version>0.0.2-SNAPSHOT</version>
</dependency>

new Retry<String>()
.maxOperationWaitTime(30_000)//Max operation wait time during a single operation
.retryIntervalTime(1_000)//Interval time between two operations
.maxRetryTimes(3)//Retry times when operation failed(or timeout) at the first time
.operation(() -> {
  //your operation
  return "success!";
})
.judgement(t -> (t == null || t.isEmpty()))//add your judgement whether the operation should be retry(Operation should return a value)
.execute();

如果要在方法上添加重试配置注释,并调用它:

class RetryTests{
    @RetryConfig( maxRetryTimes=1)
    public static String TestAnnotation() { 
        return "aaa";
    } 
    public static void main(String[] args) {
        try { 
            new Retry<String>()
            .of(RetryTest.class.getMethod("TestAnnotation"),null)
            .judgement(r -> r.equals("aaa"))
            .execute(); 
        } catch (NoSuchMethodException | SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }
 }
于 2021-11-28T14:56:19.620 回答