【问题标题】:Springboot @retryable not retryingSpringboot @retryable 不重试
【发布时间】:2016-07-05 20:49:50
【问题描述】:

以下代码未重试。我错过了什么?

@EnableRetry
@SpringBootApplication
public class App implements CommandLineRunner
{
    .........
    .........


    @Retryable()
    ResponseEntity<String> authenticate(RestTemplate restTemplate, HttpEntity<MultiValueMap<String, String>> entity) throws Exception
    {
        System.out.println("try!");
        throw new Exception();
        //return restTemplate.exchange(auth_endpoint, HttpMethod.POST, entity, String.class);
    }

我在 pom.xml 中添加了以下内容。

    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
        <version>1.1.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

我还尝试为@Retryable 提供不同的参数组合。

@Retryable(maxAttempts=10,value=Exception.class,backoff=@Backoff(delay = 2000,multiplier=2))

谢谢。

【问题讨论】:

  • 我也遇到了同样的问题。

标签: java spring maven spring-boot spring-retry


【解决方案1】:

在 spring boot 2.0.2 Release 中,我观察到 @Retryable 如果您在同一个类中有可重试和调用的方法,则它不起作用。在调试时发现切入点没有正确构建。目前,这个问题的解决方法是我们需要在不同的类中编写方法并调用它。

可以在here找到工作示例。

【讨论】:

  • 我对 spring boot 1.5.x 有同样的体验。
  • 观察到与 spring boot 2.0.0 到 2.0.5 相同。检查 mvn repo:2017 年后没有 spring-retry 版本,而 spring boot 2 于 2018 年到来。有没有一个救世主设法让 spring-retry 与任何版本的 spring-boot 2.x 一起工作?同时回滚到 Spring Boot 1.5.15,它工作正常
  • 这是一个通用的弹簧东西,你可以使用@Autowired自我引用?
  • 这就是所有 Spring 注释的工作方式。您需要一个单独的代理对象,以便 Soring 能够应用其他逻辑。
  • 这是因为 Spring 创建了一个 Proxy 对象来处理重试,它不能创建自己的代理对象。使用其他注解为@Transactional 时也会出现同样的问题
【解决方案2】:

对于要发现的方法上的@Retryable 注释,需要从初始化的上下文中正确调用它。该方法是从spring上下文中的bean调用的还是通过其他方式调用的?

如果使用SpringJunit4ClassRunner 测试这是您的跑步者?

【讨论】:

  • 谢谢,我没有机会看这个,我会回来的。
  • 这应该是公认的答案,当您说“我发现如果从您尝试重试的方法中返回某些内容,则 @Retryable() 不起作用。”错了,在retry方法中其实可以返回一个对象,重点是该方法需要从一个bean中调用。
【解决方案3】:

我解决了。我发现如果从您尝试重试的方法中返回某些内容,则 @Retryable() 不起作用。

pom.xml 中的 maven 依赖

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

Spring boot Application.java

@SpringBootApplication
@EnableTransactionManagement
@EnableRetry
public class Application {

     public static void main(String[] args) throws Exception {
       SpringApplication.run(Application.class, args);
     }

}

在controller.java中

@RestController
public class JavaAllDataTypeController {

@Autowired
JavaAllDataTypeService JavaAllDataTypeService;


@RequestMapping(
        value = "/springReTryTest",
        method = RequestMethod.GET
)
public ResponseEntity<String> springReTryTest() {

    System.out.println("springReTryTest controller");

    try {
         JavaAllDataTypeService.springReTryTest();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return new  ResponseEntity<String>("abcd", HttpStatus.OK);
  }

}

在 service.java 中

@Service
@Transactional
public class JavaAllDataTypeService {

 // try the method 9 times with 2 seconds delay.
 @Retryable(maxAttempts=9,value=Exception.class,backoff=@Backoff(delay = 2000))
 public void springReTryTest() throws Exception {

    System.out.println("try!");
    throw new Exception();
  }

}

输出:它尝试了 9 次然后抛出异常。

【讨论】:

  • 否,“从可重试方法返回值”与此问题无关。我有很多方法返回值并重试。
【解决方案4】:

它也适用于返回类型

@Service
public class RetryService {

private int count = 0;

// try the method 9 times with 2 seconds delay.
@Retryable(maxAttempts = 9, value = Exception.class, backoff = @Backoff(delay = 2000))
public String springReTryTest() throws Exception {
    count++;
    System.out.println("try!");

    if (count < 4)
        throw new Exception();
    else
        return "bla";
  }

}

【讨论】:

    【解决方案5】:

    我遇到了与原始问题中描述的完全相同的问题。

    在我的例子中,spring-boot-starter-aop 依赖被意外地不包括在内。将其添加到我的pom.xml 后,我的@Retryable 方法按预期工作。

    @Retryable 方法返回值对我来说很好。

    【讨论】:

      【解决方案6】:

      Spring 的@Retryable、@Cacheable、@Transaction 等都是使用面向方面编程实现的。 Spring 通过基于代理的编织实现 AOP。代理拦截从一个 bean 到另一个的调用。代理不能拦截从一个对象的方法到另一个对象的调用。这是基于代理的编织的一般限制。

      以下解决方案解决了这个限制:1)如上所述,使用@Autowired(或@Resource)注入一个带有自引用的bean;对该引用的调用通过代理。 2) 使用 AspectJ 的 ClassLoader 而不是 Spring 默认的基于代理的编织。 3) 如上所述,将方法放在单独的 bean 上。我在各种情况下都做过,每个都有优点和缺点。

      【讨论】:

        【解决方案7】:

        想调用@Retryable的同班同学可以这样。

        这里的关键是不要直接通过自注入bean调用方法

        @Slf4j
        @Service
        public class RetryService {
        
            @Resource(name = "retryService")
            private RetryService self;
        
            public String getValue(String appender) {
                return self.getData(appender);
            }
        
            @Retryable(value = NumberFormatException.class, maxAttempts = 4, backoff = @Backoff(500))
            public String getData(String appender) {
                log.info("Calling getData");
                Integer value = Integer.parseInt(appender);
                value++;
                return value.toString();
            }
        
            @Recover
            public String recoverData(String appender) {
                log.info("Calling recoverData");
                return "DEFAULT";
            }
        
        }
        

        可以阅读更多关于使用重试的详细信息here

        【讨论】:

          【解决方案8】:

          另一种选择是RetryTemplate

          @Bean
              public RetryTemplate retryTemplate() {
                  RetryTemplate retryTemplate = new RetryTemplate();
          
                  FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
                  fixedBackOffPolicy.setBackOffPeriod(2000l);
                  retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
          
                  SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
                  retryPolicy.setMaxAttempts(2);
                  retryTemplate.setRetryPolicy(retryPolicy);
          
                  return retryTemplate;
              }
          

          retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
              @Override
              public Void doWithRetry(RetryContext arg0) {
                  myService.templateRetryService();
                  ...
              }
          });
          

          帮我搞定

          source

          【讨论】:

            【解决方案9】:
            @RequiredArgsConstructor
            **@EnableRetry**
            @Service
            public class SomeService {
            
              **@Retryable(value = { HttpServerErrorException.class, BadRequestException.class},
                  maxAttempts = maxRetry, backoff = @Backoff(random = true, delay = 1000,
                                     maxDelay = 8000, multiplier = 2))**
              public <T> T get( ) throws  HttpServerErrorException, BadRequestException {
                
                 //write code here which you want to retry
              }
            
            }
            

            我希望这将有助于解决您的问题。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-12-30
              相关资源
              最近更新 更多