admin管理员组

文章数量:1355581

I am using Spring Retry by creating a RetryTemplate as follows:

import com.example.MyRetryProperties;
import com.example.SomeRetryableException;
import lombok.RequiredArgsConstructor;
import .springframework.context.annotation.Bean;
import .springframework.context.annotation.Configuration;
import .springframework.retry.annotation.EnableRetry;
import .springframework.retry.support.RetryTemplate;

@Configuration
@EnableRetry
@RequiredArgsConstructor
public class RetryConfiguration {

    private final MyRetryProperties retryProperties;


    @Bean
    public RetryTemplate retryTemplate() {
        return RetryTemplate.builder()
                .exponentialBackoff(
                        retryProperties.getInitialInterval(), // 500ms
                        retryProperties.getMultiplier(), // 2.0
                        retryProperties.getMaxInterval(),  // 2000ms
                        false)
                .withTimeout(retryProperties.getTimeout())
                .retryOn(MyRetryableException.class)
                .traversingCauses()
                .build();
    }
}

If a retry is in progress, I know that I can access the current retry count using RetryContext#getRetryCount. Is it possible somehow to access the current interval value without calculating it based on the retry count (e.g. also from the context)?

import .springframework.retry.RetryCallback;
import .springframework.retry.RetryContext;
import .springframework.retry.RetryListener;

public class MyRetryListener implements RetryListener {

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

        int retryCount = context.getRetryCount();
        System.out.println("Current retryCount: " + retryCount); // e.g. 3


        int currentRetryInterval = // how?
        System.out.println("Current retry interval: " + currentRetryInterval); // e.g. 2000ms for the third retry
    }
}

I am using Spring Retry by creating a RetryTemplate as follows:

import com.example.MyRetryProperties;
import com.example.SomeRetryableException;
import lombok.RequiredArgsConstructor;
import .springframework.context.annotation.Bean;
import .springframework.context.annotation.Configuration;
import .springframework.retry.annotation.EnableRetry;
import .springframework.retry.support.RetryTemplate;

@Configuration
@EnableRetry
@RequiredArgsConstructor
public class RetryConfiguration {

    private final MyRetryProperties retryProperties;


    @Bean
    public RetryTemplate retryTemplate() {
        return RetryTemplate.builder()
                .exponentialBackoff(
                        retryProperties.getInitialInterval(), // 500ms
                        retryProperties.getMultiplier(), // 2.0
                        retryProperties.getMaxInterval(),  // 2000ms
                        false)
                .withTimeout(retryProperties.getTimeout())
                .retryOn(MyRetryableException.class)
                .traversingCauses()
                .build();
    }
}

If a retry is in progress, I know that I can access the current retry count using RetryContext#getRetryCount. Is it possible somehow to access the current interval value without calculating it based on the retry count (e.g. also from the context)?

import .springframework.retry.RetryCallback;
import .springframework.retry.RetryContext;
import .springframework.retry.RetryListener;

public class MyRetryListener implements RetryListener {

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

        int retryCount = context.getRetryCount();
        System.out.println("Current retryCount: " + retryCount); // e.g. 3


        int currentRetryInterval = // how?
        System.out.println("Current retry interval: " + currentRetryInterval); // e.g. 2000ms for the third retry
    }
}
Share Improve this question edited Mar 31 at 21:14 Geii Lvov asked Mar 31 at 10:25 Geii LvovGeii Lvov 2,9181 gold badge8 silver badges29 bronze badges 9
  • 1 No. The RetryContext doesn't know anything about the strategy that is being used so the only common denominator you can access is the retryCount. So the only way is to do the calculation yourself, which means your listener needs to know which BackOffPolicy is being used. – M. Deinum Commented Mar 31 at 11:19
  • @M.Deinum, okay, thank you. You can formalize your comment as an answer and I will accept it – Geii Lvov Commented Mar 31 at 11:26
  • @M.Deinum, I would also appreciate some tips about how to tell the RetryListener about Backoff policy (in case there are other options except pulling all intervals from application.properties directly). Because I tried to get backOffContext attribute from the context, which is apparently ExponentialBackOffPolicy.ExponentialBackOffContext, but is not public to pull values from there. – Geii Lvov Commented Mar 31 at 11:43
  • Not sure, it will probably involve some trickery with reflection. But even with that you probably aren't getting the next interval without affecting the actual execution (at least looking at the code from the policies). – M. Deinum Commented Mar 31 at 12:12
  • See RetryTemplate source code. The doOnErrorInterceptors happens before backOffPolicy.backOff(backOffContext);. So, maximum you can get from your listener callback is an info from the previous retry attempt. So, what you are asking is not possible by design. The currently calculate interval is used immediately in the ExponentialBackOffPolicy for this.sleeper.sleep(sleepTime); – Artem Bilan Commented Mar 31 at 15:30
 |  Show 4 more comments

1 Answer 1

Reset to default 3

The RetryContext doesn't know anything about the strategy that is being used so the only common denominator you can access is the retryCount. The only way is to do the calculation yourself, which means your listener needs to know which BackOffPolicy is being used.

You could use the getAttribute from the RetryContext to get the BackOffContext, but trying to cast it to the actual one will probably fail.

You might need to use reflection to actually get access to the field containing the calculated interval.

import .springframework.retry.RetryCallback;
import .springframework.retry.RetryContext;
import .springframework.retry.RetryListener;
import .springframework.util.ReflectionUtils;

public class MyRetrListener implements RetryListener {

    @Override
    public <T, E extends Throwable> void onError(RetryContext context,
                                                 RetryCallback<T, E> callback,
                                                 Throwable throwable) {
        Object backOffPolicy = context.getAttribute("backOffContext");

        var intervalField = ReflectionUtils.findField(backOffPolicy.getClass(), "interval");
        var maxIntervalField = ReflectionUtils.findField(backOffPolicy.getClass(), "maxInterval");

        intervalField.setAccessible(true);
        maxIntervalField.setAccessible(true);

        long interval = (long) ReflectionUtils.getField(intervalField, backOffPolicy);
        long maxInterval = (long) ReflectionUtils.getField(maxIntervalField, backOffPolicy);

        long currentRetryInterval = Math.min(interval, maxInterval);

        System.out.println("Retry count: " + context.getRetryCount());
        System.out.println("Current retry interval: " + currentRetryInterval);
    }
}

Result for 4 retries as expected:

Retry count: 1
Current retry interval: 500
Retry count: 2
Current retry interval: 1000
Retry count: 3
Current retry interval: 2000
Retry count: 4
Current retry interval: 2000

本文标签: javaHow to access current retry interval valueStack Overflow