欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Springboot Retry组件@Recover失效问题解决方法

程序员文章站 2022-03-13 09:53:05
目录背景问题复现问题解决背景在使用springboot的retry模块时,你是否出现过@recover注解失效的问题呢?下面我会对该问题进行复现,并且简要的说下解决方法。问题复现首先我们引入maven...

背景

在使用springboot的retry模块时,你是否出现过@recover注解失效的问题呢?下面我会对该问题进行复现,并且简要的说下解决方法。

问题复现

首先我们引入maven

        <dependency>
            <groupid>org.springframework.retry</groupid>
            <artifactid>spring-retry</artifactid>
        </dependency>
        <dependency>
            <groupid>org.aspectj</groupid>
            <artifactid>aspectjweaver</artifactid>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupid>cn.hutool</groupid>
            <artifactid>hutool-all</artifactid>
            <version>5.5.2</version>
        </dependency>
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>

主类配置enableretry注解

package ai.guiji.csdn;
 
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
import org.springframework.retry.annotation.enableretry;
 
@enableretry
@springbootapplication
public class csdnapplication {
 
  public static void main(string[] args) {
    springapplication.run(csdnapplication.class, args);
  }
}

准备测试的retry组件类代码

package ai.guiji.csdn.component;
 
import lombok.extern.slf4j.slf4j;
import org.springframework.retry.annotation.backoff;
import org.springframework.retry.annotation.recover;
import org.springframework.retry.annotation.retryable;
import org.springframework.stereotype.component;
 
import java.util.function.supplier;
 
/** @author 剑客阿良_aliang @date 2021/4/22 16:07 @description: 重试工具 */
@slf4j
@component
public class retryutil {
  @retryable(
      value = exception.class,
      maxattempts = 3,
      backoff = @backoff(delay = 5000, multiplier = 1.5))
  public string retry(supplier<string> supplier) throws exception {
    string result = null;
    try {
      result = supplier.get();
    } catch (exception exception) {
      log.error("异常报错:{}", exception.getmessage());
      throw exception;
    }
    return result;
  }
 
  @recover
  public void recover(exception e) {
    log.error("调用超过3次异常");
  }
}

代码说明

1、我们可以看到retry方法会重试supplier的get结果,捕获异常并抛出异常。这里抛出后会被retry捕获并且重试。

2、maxattempts参数为重试的最大次数。

3、backoff中的delay为两次重试之间的延迟,multiplier为重试阻尼,可以这么理解,每次重试间隔时间为上一次重试间隔时间的倍数。

4、如果3次重试均抛出异常,则进入recover方法。

编写测试代码

package ai.guiji.csdn.component;
 
import cn.hutool.core.convert.convert;
import cn.hutool.http.httputil;
import org.junit.jupiter.api.test;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.boot.test.context.springboottest;
 
/** @author 剑客阿良_alaing @date 2021/11/30 13:08 @description: */
@springboottest
class retryutiltest {
  @autowired private retryutil retryutil;
 
  @test
  void retry() {
    try {
      system.out.println(convert.tostr(retryutil.retry(() -> httputil.post("xxxx", "")), "haha"));
    } catch (exception exception) {
      exception.printstacktrace();
    }
  }
}

执行测试结果

2021-11-30 13:37:44.012 error 13600 --- [           main] ai.guiji.csdn.component.retryutil        : 异常报错:unknownhostexception: xxxx
2021-11-30 13:37:49.019 error 13600 --- [           main] ai.guiji.csdn.component.retryutil        : 异常报错:unknownhostexception: xxxx
2021-11-30 13:37:58.787 error 13600 --- [           main] ai.guiji.csdn.component.retryutil        : 异常报错:unknownhostexception: xxxx
org.springframework.retry.exhaustedretryexception: cannot locate recovery method; nested exception is cn.hutool.core.io.ioruntimeexception: unknownhostexception: xxxx
	at org.springframework.retry.annotation.recoverannotationrecoveryhandler.recover(recoverannotationrecoveryhandler.java:70)
	at org.springframework.retry.interceptor.retryoperationsinterceptor$itemrecoverercallback.recover(retryoperationsinterceptor.java:142)
	at org.springframework.retry.support.retrytemplate.handleretryexhausted(retrytemplate.java:539)
	at org.springframework.retry.support.retrytemplate.doexecute(retrytemplate.java:387)
	at org.springframework.retry.support.retrytemplate.execute(retrytemplate.java:225)
	at org.springframework.retry.interceptor.retryoperationsinterceptor.invoke(retryoperationsinterceptor.java:116)
	at org.springframework.retry.annotation.annotationawareretryoperationsinterceptor.invoke(annotationawareretryoperationsinterceptor.java:163)
	at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:186)
	at org.springframework.aop.framework.cglibaopproxy$cglibmethodinvocation.proceed(cglibaopproxy.java:750)
	at org.springframework.aop.framework.cglibaopproxy$dynamicadvisedinterceptor.intercept(cglibaopproxy.java:692)
	at ai.guiji.csdn.component.retryutil$$enhancerbyspringcglib$$d209cbc6.retry(<generated>)
	at ai.guiji.csdn.component.retryutiltest.retry(retryutiltest.java:17)
	at sun.reflect.nativemethodaccessorimpl.invoke0(native method)
	at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)
	at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43)
	at java.lang.reflect.method.invoke(method.java:498)
	at org.junit.platform.commons.util.reflectionutils.invokemethod(reflectionutils.java:688)
	at org.junit.jupiter.engine.execution.methodinvocation.proceed(methodinvocation.java:60)
	at org.junit.jupiter.engine.execution.invocationinterceptorchain$validatinginvocation.proceed(invocationinterceptorchain.java:131)
	at org.junit.jupiter.engine.extension.timeoutextension.intercept(timeoutextension.java:149)
	at org.junit.jupiter.engine.extension.timeoutextension.intercepttestablemethod(timeoutextension.java:140)
	at org.junit.jupiter.engine.extension.timeoutextension.intercepttestmethod(timeoutextension.java:84)
	at org.junit.jupiter.engine.execution.executableinvoker$reflectiveinterceptorcall.lambda$ofvoidmethod$0(executableinvoker.java:115)
	at org.junit.jupiter.engine.execution.executableinvoker.lambda$invoke$0(executableinvoker.java:105)
	at org.junit.jupiter.engine.execution.invocationinterceptorchain$interceptedinvocation.proceed(invocationinterceptorchain.java:106)
	at org.junit.jupiter.engine.execution.invocationinterceptorchain.proceed(invocationinterceptorchain.java:64)
	at org.junit.jupiter.engine.execution.invocationinterceptorchain.chainandinvoke(invocationinterceptorchain.java:45)
	at org.junit.jupiter.engine.execution.invocationinterceptorchain.invoke(invocationinterceptorchain.java:37)
	at org.junit.jupiter.engine.execution.executableinvoker.invoke(executableinvoker.java:104)
	at org.junit.jupiter.engine.execution.executableinvoker.invoke(executableinvoker.java:98)
	at org.junit.jupiter.engine.descriptor.testmethodtestdescriptor.lambda$invoketestmethod$6(testmethodtestdescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.jupiter.engine.descriptor.testmethodtestdescriptor.invoketestmethod(testmethodtestdescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.testmethodtestdescriptor.execute(testmethodtestdescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.testmethodtestdescriptor.execute(testmethodtestdescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$5(nodetesttask.java:139)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$7(nodetesttask.java:129)
	at org.junit.platform.engine.support.hierarchical.node.around(node.java:137)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$8(nodetesttask.java:127)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.executerecursively(nodetesttask.java:126)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.execute(nodetesttask.java:84)
	at java.util.arraylist.foreach(arraylist.java:1257)
	at org.junit.platform.engine.support.hierarchical.samethreadhierarchicaltestexecutorservice.invokeall(samethreadhierarchicaltestexecutorservice.java:38)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$5(nodetesttask.java:143)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$7(nodetesttask.java:129)
	at org.junit.platform.engine.support.hierarchical.node.around(node.java:137)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$8(nodetesttask.java:127)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.executerecursively(nodetesttask.java:126)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.execute(nodetesttask.java:84)
	at java.util.arraylist.foreach(arraylist.java:1257)
	at org.junit.platform.engine.support.hierarchical.samethreadhierarchicaltestexecutorservice.invokeall(samethreadhierarchicaltestexecutorservice.java:38)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$5(nodetesttask.java:143)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$7(nodetesttask.java:129)
	at org.junit.platform.engine.support.hierarchical.node.around(node.java:137)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.lambda$executerecursively$8(nodetesttask.java:127)
	at org.junit.platform.engine.support.hierarchical.throwablecollector.execute(throwablecollector.java:73)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.executerecursively(nodetesttask.java:126)
	at org.junit.platform.engine.support.hierarchical.nodetesttask.execute(nodetesttask.java:84)
	at org.junit.platform.engine.support.hierarchical.samethreadhierarchicaltestexecutorservice.submit(samethreadhierarchicaltestexecutorservice.java:32)
	at org.junit.platform.engine.support.hierarchical.hierarchicaltestexecutor.execute(hierarchicaltestexecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.hierarchicaltestengine.execute(hierarchicaltestengine.java:51)
	at org.junit.platform.launcher.core.engineexecutionorchestrator.execute(engineexecutionorchestrator.java:108)
	at org.junit.platform.launcher.core.engineexecutionorchestrator.execute(engineexecutionorchestrator.java:88)
	at org.junit.platform.launcher.core.engineexecutionorchestrator.lambda$execute$0(engineexecutionorchestrator.java:54)
	at org.junit.platform.launcher.core.engineexecutionorchestrator.withinterceptedstreams(engineexecutionorchestrator.java:67)
	at org.junit.platform.launcher.core.engineexecutionorchestrator.execute(engineexecutionorchestrator.java:52)
	at org.junit.platform.launcher.core.defaultlauncher.execute(defaultlauncher.java:96)
	at org.junit.platform.launcher.core.defaultlauncher.execute(defaultlauncher.java:75)
	at com.intellij.junit5.junit5ideatestrunner.startrunnerwithargs(junit5ideatestrunner.java:69)
	at com.intellij.rt.junit.ideatestrunner$repeater.startrunnerwithargs(ideatestrunner.java:33)
	at com.intellij.rt.junit.junitstarter.preparestreamsandstart(junitstarter.java:230)
	at com.intellij.rt.junit.junitstarter.main(junitstarter.java:58)
caused by: cn.hutool.core.io.ioruntimeexception: unknownhostexception: xxxx
	at cn.hutool.http.httprequest.send(httprequest.java:1153)
	at cn.hutool.http.httprequest.execute(httprequest.java:969)
	at cn.hutool.http.httprequest.execute(httprequest.java:940)
	at cn.hutool.http.httputil.post(httputil.java:216)
	at cn.hutool.http.httputil.post(httputil.java:197)
	at ai.guiji.csdn.component.retryutiltest.lambda$retry$0(retryutiltest.java:17)
	at ai.guiji.csdn.component.retryutil.retry(retryutil.java:22)
	at ai.guiji.csdn.component.retryutil$$fastclassbyspringcglib$$a565f63f.invoke(<generated>)
	at org.springframework.cglib.proxy.methodproxy.invoke(methodproxy.java:218)
	at org.springframework.aop.framework.cglibaopproxy$cglibmethodinvocation.invokejoinpoint(cglibaopproxy.java:779)
	at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:163)
	at org.springframework.aop.framework.cglibaopproxy$cglibmethodinvocation.proceed(cglibaopproxy.java:750)
	at org.springframework.retry.interceptor.retryoperationsinterceptor$1.dowithretry(retryoperationsinterceptor.java:93)
	at org.springframework.retry.support.retrytemplate.doexecute(retrytemplate.java:329)
	... 73 more
caused by: java.net.unknownhostexception: xxxx
	at java.net.abstractplainsocketimpl.connect(abstractplainsocketimpl.java:184)
	at java.net.plainsocketimpl.connect(plainsocketimpl.java:172)
	at java.net.sockssocketimpl.connect(sockssocketimpl.java:392)
	at java.net.socket.connect(socket.java:589)
	at java.net.socket.connect(socket.java:538)
	at sun.net.networkclient.doconnect(networkclient.java:180)
	at sun.net.www.http.httpclient.openserver(httpclient.java:463)
	at sun.net.www.http.httpclient.openserver(httpclient.java:558)
	at sun.net.www.http.httpclient.<init>(httpclient.java:242)
	at sun.net.www.http.httpclient.new(httpclient.java:339)
	at sun.net.www.http.httpclient.new(httpclient.java:357)
	at sun.net.www.protocol.http.httpurlconnection.getnewhttpclient(httpurlconnection.java:1220)
	at sun.net.www.protocol.http.httpurlconnection.plainconnect0(httpurlconnection.java:1156)
	at sun.net.www.protocol.http.httpurlconnection.plainconnect(httpurlconnection.java:1050)
	at sun.net.www.protocol.http.httpurlconnection.connect(httpurlconnection.java:984)
	at sun.net.www.protocol.http.httpurlconnection.getoutputstream0(httpurlconnection.java:1334)
	at sun.net.www.protocol.http.httpurlconnection.getoutputstream(httpurlconnection.java:1309)
	at cn.hutool.http.httpconnection.getoutputstream(httpconnection.java:451)
	at cn.hutool.http.httprequest.sendformurlencoded(httprequest.java:1176)
	at cn.hutool.http.httprequest.send(httprequest.java:1145)
	... 86 more
 
 
process finished with exit code 0

并没有进入recover方法,注解未触发。

问题解决

这里有个很容易忽视的点,就是retry方法是有返回值的,所以recover方法也必须是相同类型带返回值的方法。所以要把recover方法改一下。

package ai.guiji.csdn.component;
 
import lombok.extern.slf4j.slf4j;
import org.springframework.retry.annotation.backoff;
import org.springframework.retry.annotation.recover;
import org.springframework.retry.annotation.retryable;
import org.springframework.stereotype.component;
 
import java.util.function.supplier;
 
/** @author 剑客阿良_aliang @date 2021/4/22 16:07 @description: 重试工具 */
@slf4j
@component
public class retryutil {
  @retryable(
      value = exception.class,
      maxattempts = 3,
      backoff = @backoff(delay = 5000, multiplier = 1.5))
  public string retry(supplier<string> supplier) throws exception {
    string result = null;
    try {
      result = supplier.get();
    } catch (exception exception) {
      log.error("异常报错:{}", exception.getmessage());
      throw exception;
    }
    return result;
  }
 
  @recover
  public string recover(exception e) {
    log.error("调用超过3次异常");
    return "调用超过3次异常";
  }
}

重新执行测试看下结果

Springboot Retry组件@Recover失效问题解决方法

以上就是springboot retry组件@recover失效问题解决方法的详细内容,更多关于springboot retry 解决@recover失效的资料请关注其它相关文章!