[英]How to perform arithmentic operation in delayExpression in @Backoff (Spring-retry)
[英]Specifying an exception-specific backoff policy with Spring-Retry
我正在使用Spring-Retry進行一些數據庫操作。 在SQLRecoverableException
我重試了 3 次(這假設導致異常的原因是非暫時性的,如果它失敗了 3 次),在SQLTransientException
我無限期地重試(程序不能在沒有訪問數據庫的情況下做任何事情,所以它可能並繼續重試,直到用戶決定重新啟動服務器),並且在任何其他異常情況下我不會重試。 我使用指數退避策略,基本重試時間為 100 毫秒,最大重試時間為 30,000 毫秒。
private static final int MAX_RECOVERABLE_RETRIES = 3;
private static final long INITIAL_INTERVAL = 100;
private static final long MAX_INTERVAL = 30 * 1000;
private static final double MULTIPLIER = 2.0;
public static RetryTemplate databaseTemplate() {
RetryTemplate template = new RetryTemplate();
ExceptionClassifierRetryPolicy retryPolicy = new ExceptionClassifierRetryPolicy();
Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<>();
NeverRetryPolicy baseException = new NeverRetryPolicy();
SimpleRetryPolicy recoverablePolicy = new SimpleRetryPolicy();
recoverablePolicy.setMaxAttempts(MAX_RECOVERABLE_RETRIES);
AlwaysRetryPolicy transientPolicy = new AlwaysRetryPolicy();
policyMap.put(Exception.class, baseException);
policyMap.put(SQLRecoverableException.class, recoverablePolicy);
policyMap.put(SQLTransientException.class, transientPolicy);
retryPolicy.setPolicyMap(policyMap);
template.setRetryPolicy(retryPolicy);
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(INITIAL_INTERVAL);
backOffPolicy.setMaxInterval(MAX_INTERVAL);
backOffPolicy.setMultiplier(MULTIPLIER);
template.setBackOffPolicy(backOffPolicy);
return template;
}
理想情況下,我想對所有SQLRecoverableExceptions
使用 100 毫秒的固定退避,並且只將指數退避策略應用於SQLTransientExceptions
。 我可以通過嵌套重試來實現這一點,但這會大大增加代碼的復雜性——如果沒有其他選擇,我寧願簡單地將指數退避應用於SQLRecoverableException
和SQLTransientException
異常。
有沒有辦法使用單個重試模板將不同的退避策略應用於不同的異常?
確實, ExceptionClassifierRetryPolicy
是要走的路。 不過,我沒有設法讓它與policyMap
一起使用。
這是我如何使用它:
@Component("yourRetryPolicy")
public class YourRetryPolicy extends ExceptionClassifierRetryPolicy
{
@PostConstruct
public void init()
{
final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts( 3 );
this.setExceptionClassifier( new Classifier<Throwable, RetryPolicy>()
{
@Override
public RetryPolicy classify( Throwable classifiable )
{
if ( classifiable instanceof YourException )
{
return new NeverRetryPolicy();
}
// etc...
return simpleRetryPolicy;
}
});
}
}
然后,您只需在重試模板上設置它:
@Autowired
@Qualifier("yourRetryPolicy")
private YourRetryPolicy yourRetryPolicy;
//...
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy( yourRetryPolicy );
接受的答案僅處理特定於異常的 RetryPolicy 實例。 Spring 沒有為特定於異常的 BackOffPolicy 實例提供任何開箱即用的功能。 幸運的是,它很容易實現。
import org.springframework.classify.Classifier
import org.springframework.classify.ClassifierSupport
import org.springframework.classify.SubclassClassifier
import org.springframework.retry.RetryContext
import org.springframework.retry.backoff.BackOffContext
import org.springframework.retry.backoff.BackOffInterruptedException
import org.springframework.retry.backoff.BackOffPolicy
import org.springframework.retry.backoff.NoBackOffPolicy
class ExceptionClassifierBackoffPolicy implements BackOffPolicy {
private static class ExceptionClassifierBackoffContext implements BackOffContext, BackOffPolicy {
Classifier<Throwable, BackOffPolicy> exceptionClassifier
RetryContext retryContext
Map<BackOffPolicy, BackOffContext> policyContextMap = [:]
ExceptionClassifierBackoffContext(Classifier<Throwable, BackOffPolicy> exceptionClassifier, RetryContext retryContext) {
this.exceptionClassifier = exceptionClassifier
this.retryContext = retryContext
}
@Override
BackOffContext start(RetryContext context) {
return null
}
@Override
void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
def policy = exceptionClassifier.classify(retryContext.lastThrowable)
def policyContext = policyContextMap.get(policy)
if (!policyContext) {
policyContext = policy.start(retryContext)
policyContextMap.put(policy, policyContext)
}
policy.backOff(policyContext)
}
}
private Classifier<Throwable, BackOffPolicy> exceptionClassifier = new ClassifierSupport<Throwable, BackOffPolicy>(new NoBackOffPolicy());
void setPolicyMap(Map<Class<? extends Throwable>, BackOffPolicy> policyMap) {
exceptionClassifier = new SubclassClassifier<Throwable, BackOffPolicy>(policyMap, new NoBackOffPolicy());
}
@Override
BackOffContext start(RetryContext context) {
return new ExceptionClassifierBackoffContext(exceptionClassifier, context)
}
@Override
void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
def classifierBackOffContext = (ExceptionClassifierBackoffContext) backOffContext
classifierBackOffContext.backOff(backOffContext)
}
}
那么只需:
BackOffPolicy backOffPolicy = new ExceptionClassifierBackoffPolicy()
def policyMap = [
(RuntimeException): new FixedBackOffPolicy(backOffPeriod: 1000),
(IOException) : new ExponentialRandomBackOffPolicy(initialInterval: 500, maxInterval: 360000, multiplier: 2)
] as Map<Class<? extends Throwable>, BackOffPolicy>
backOffPolicy.policyMap = backoffPolicyMap
聚會有點晚了,但是當您需要的是根據異常類型有不同的退避期時,擴展FixedBackoffPolicy
應該可以解決問題並且非常簡單。 沿着這些路線的東西:
首先,您創建退避策略類,該類接收每個異常類型具有不同退避周期的映射:
public class MultipleExceptionsBackoffPolicy extends FixedBackoffPolicy
{
private Classifier<Throwable, Long> classifier;
public MultipleExceptionsBackoffPolicy (final Map<Class<? extends Throwable>, Long> throwableBackoffMap) {
classifier = new SubclassClassifier<>(throwableBackoffMap, 5_000L) // default is 5s
}
@Override
protected void doBackOff() exception BackOffInterruptedException {
final backoff = classifier.classify(RetrySynchronizationManager.getContext().getLastThrowable());
setBackOffPeriod(backoff);
super.doBackOff();
}
}
那么你必須創建一個自定義攔截器,例如:
@Bean
public Object myCustomInterceptor (){
var exBackoffMap = Map.of(
ExceptionTypeOne.class, 2_000L, // If ExceptionTypeOne happens, backoff period is 2s
ExceptionTypeTwo.class, 7_000L // and if ExceptionTypeTwo happens, the backoff period is 7s
)
return RetryInterceptorBuilder
.stateless()
.retryPolicy(new SimpleRetryPolicy(3)) // always 3 attempts no matter what
.backOffPolicy(new MultipleExceptionsBackoffPolicy(exBackoffMap))
.build();
}
最后,您只需在可重試中配置自定義攔截器:
@Retryable(interceptor="myCustomInterceptor")
public @interface MyRetryable {}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.