弹簧重试在第二级方法上不起作用

2022-09-03 07:19:09

@Retryable似乎没有像下面这样研究第二级方法。我看到创建了一个代理,但失败时永远不会重试。sphRemoteCall

一旦我移动到第一级方法,比如,它就开始工作了。@RetryablegetSubscriberAccount

示例如下:

@Service
public class SphIptvClient extends WebServiceGatewaySupport {
    //Works over here
    @Retryable(maxAttempts=3, backoff=@Backoff(delay=100))
    public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {

        GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
        return response;
    }

    /*
     * Retryable is not working on the 2nd level methods in the bean. 
     * It works only with methods which are called directly from outside
     * if there is 2nd level method, like this, Retryable is not working.
     */
    //@Retryable
    private Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
        log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
        return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
    }
}

@Configuration
@EnableRetry
public class SphClientConfig {
    @Bean
    public SphIptvClient sphIptvClient() {
        SphIptvClient client = new SphIptvClient();
        return client;
    }
}

答案 1

所以这是一个非常晚的答案,但是由于我刚刚来到这里并面临同样的问题(再次,在几年前与交易搏斗之后),我将提供一个更充实的解决方案,希望有人会发现它有用。可以说,@M. Deinum的诊断是正确的。

enter image description here

enter image description here

在上面的例子中,为了解释理解AOP代理,任何自动连接的地方都会被赋予对代理的引用,Spring Retry将在处理时创建该代理:SphIptvClient@EnableRetry

“注释为 Bean 创建代理” - 声明性重试 - Spring 重试@EnableRetry@Retryable

一旦被调用并且执行已通过代理并进入对象的实例,则对代理的引用是未知的。结果被调用,就好像根本没有一样。getSubscriberAccount@ServicesphRemoteCall@Retryable

你可以通过以这样的方式对代码进行洗牌来使用框架,以便允许调用代理ed,这需要一个新的接口和类实现。getSubscriberAccountsphRemoteCall

例如:

public interface SphWebService {
   Object sphRemoteCall(String uri, Object requestPayload, String soapAction);
}

@Component
public class SphWebServiceImpl implements SphWebService {
   @Retryable
   public Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
       log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
       return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
   }
}

@Service
public class SphIptvClient extends WebServiceGatewaySupport {

   @Autowired
   SphWebService sphWebService;

   @Retryable(maxAttempts=3, backoff=@Backoff(delay=100))
   public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {
       GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) this.sphWebService.sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
       return response;
   }
}

@Configuration
@EnableRetry
public class SphClientConfig {
   // the @Bean method was unnecessary and may cause confusion. 
   // @Service was already instantiating SphIptvClient behind the scenes.
}

答案 2

@Retryable仅适用于直接从其他类调用的方法。如果您尝试从同一类的其他方法调用一个具有@Retryable注释的方法,则它最终将不起作用。

    // any call from this method to test method will not invoke the retry logic.
    public void yetAnotherMethod() {
        this.test();
    }

    // it will work
    @Retryable(value = {RuntimeException.class}, backoff = @Backoff(delay = 1500))
    public void test() {
        System.out.println("Count: " + count++);
        throw new RuntimeException("testing");
    }

    @Recover
    public void recover() {
        System.out.println("Exception occured.");
    }

因此,如果调用测试方法,则输出将为:

计数: 0
计数: 1
计数: 2
发生异常。

但是,如果调用 yetOtherMethod,则输出将为:
Count: 0
并且将引发运行时异常。


推荐