简体   繁体   English

Spring注释@Retryable - 如何设置拦截器

[英]Spring annotation @Retryable - how to set an interceptor

I am using a @Retryable annotation on a method in a @Service class 我在@Service类的方法上使用@Retryable注释

@Service
@EnableRetry 
public class PushService {

    @Retryable(maxAttempts=5)
    public Result pushIt(myMessage messageIn) {
        ...
    }
}

and it works like a charme: I am getting a message directly from RabbitMQ, it is not acknowledged until either there is no error, or the number of attempts reach 5, and at that time the messages goes straight to the DLQ, right as I wanted. 它就像一个魅力:我直接从RabbitMQ收到一条消息,直到没有错误或者尝试次数达到5次才会被确认,并且当时消息直接进入DLQ,就像我一样通缉。

My only problem is that I need to set the maxAttempts dynamically, from a property file. 我唯一的问题是我需要从属性文件中动态设置maxAttempts。 The solution should be setting an interceptor, but the only fact of having one causes an error, for example when I have : 解决方案应该是设置一个拦截器,但是唯一一个导致错误的事实,例如当我有:

@Service
@EnableRetry 
public class PushService {

    @Retryable(interceptor="myInterceptor") 
    public Result pushIt(myMessage messageIn) {
        ...
    }
}

where myInterceptor is defined as : 其中myInterceptor定义为:

@Bean
public StatefulRetryOperationsInterceptor myInterceptor() {
    return RetryInterceptorBuilder.stateful().maxAttempts(5).build();
}

I get an infinite loop with the following exception: 我得到一个无限循环,但有以下异常:

2015-04-08 07:12:10,970 GMT [SimpleAsyncTaskExecutor-1] (ConditionalRejectingErrorHandler.java:67) WARN  listener.ConditionalRejectingErrorHandler: Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:864)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:802)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:690)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:82)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:167)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1241)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1005)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:989)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:82)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1103)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
    at org.springframework.amqp.rabbit.config.StatefulRetryOperationsInterceptorFactoryBean$3.getKey(StatefulRetryOperationsInterceptorFactoryBean.java:103)
    at org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor.invoke(StatefulRetryOperationsInterceptor.java:132)
    at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:118)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
    at com.acme.push.service.PushService$$EnhancerBySpringCGLIB$$9d503bc1.pushMessage(<generated>)
    at com.acme.push.receiver.PushListener.onMessage(PushListener.java:42)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:799)
    ... 10 more

I am pretty sure I am keeping it too simple, but I just have no clues on what could cause this error and how to solve it, anybody has an idea of what is going on ? 我很确定我保持它太简单了,但我没有找到可能导致此错误的原因的线索以及如何解决它,任何人都知道发生了什么?

The purpose of org.springframework.amqp.rabbit.config.RetryInterceptorBuilder is not to be used using the annotation @Retryable . 不使用注释@Retryable来使用org.springframework.amqp.rabbit.config.RetryInterceptorBuilder的目的。

Its purpose is to be used with the advice chain of the SimpleRabbitListenerContainerFactory class. 其目的是与SimpleRabbitListenerContainerFactory类的建议链一起使用。 See the reference documentation receiving-messages and he SimpleMessageListenerContainer#invokeListener signature: 请参阅参考文档接收消息SimpleMessageListenerContainer#invokeListener签名:

@Override
protected void invokeListener(Channel channel, Message message) throws Exception {
    proxy.invokeListener(channel, message);
}

You annotation will be useless as soon as you configure the advice chain with the appropriate RetryInterceptor . 一旦使用适当的RetryInterceptor配置建议链,您的注释将毫无用处。

I finally managed to obtain the needed flexibility without using the @Retrayable annotation. 我终于设法在不使用@Retrayable注释的情况下获得了所需的灵活性。

I created a RetryAdvice with my parameters for delay and maximum number of attempts: 我创建了一个RetryAdvice其中包含延迟和最大尝试次数的参数:

@Bean
public MethodInterceptor retryAdvice() {
    FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
    backOffPolicy.setBackOffPeriod(delay);
    return RetryInterceptorBuilder.stateless().backOffPolicy(backOffPolicy)
            .maxAttempts(maxAttempts).build();
}

and I inserted the Advice in the adviceChain of the ListenerContainer 然后我在ListenerContainer的adviceChain中插入了Advice

@Bean
public SimpleMessageListenerContainer replyListenerContainer() {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(pushConnectionFactory());
    container.setQueues(pushQueue());
    container.setMessageListener(pushListener());

    Advice[] adviceChain = new Advice[] { retryAdvice() };
    container.setAdviceChain(adviceChain);

    return container;
}

In this way, whenever my Listener will be throwing 这样,每当我的倾听者投掷时

throw new AmqpRejectAndDontRequeueException(cause);

this will cause the container to retry the indicated number of times with the desired delay, after which the exception will be propagated and the message will be delivered in the DLQ 这将导致容器以所需的延迟重试指定的次数,之后将传播异常并且消息将在DLQ中传递

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM