【问题标题】:Retrying Method calls in generic way以通用方式重试方法调用
【发布时间】:2012-06-10 21:53:56
【问题描述】:

我的 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
}

【问题讨论】:

    标签: java reflection frameworks openframeworks


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      这是一个可以使用(或一般的)的书籍示例,请参阅8.2.7 Example in Spring documentation5 Reasons Java Developers Should Learn and Use AspectJ

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

      【讨论】:

        【解决方案3】:

        如前所述,您应该使用 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

        【讨论】:

          【解决方案4】:

          假设您有一个方法,该方法需要每 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;
                  } 
               }
          }
          

          【讨论】:

            【解决方案5】:

            enter link description hereSpring 有一个重试注解,它服务于目的

            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
            

            【讨论】:

              【解决方案6】:

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

              【讨论】:

                【解决方案7】:

                我没有找到我需要的东西,所以有我的。 主要特点是它会在达到 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
                                ));
                

                【讨论】:

                  【解决方案8】:

                  将其添加到 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();
                          } 
                      }
                   }
                  

                  【讨论】:

                    猜你喜欢
                    • 2012-07-12
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-01-28
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-02-04
                    相关资源
                    最近更新 更多