Java 在AMQP的Spring集成中使用ImmediateRequeueMessageRecoverer?

Java 在AMQP的Spring集成中使用ImmediateRequeueMessageRecoverer?,java,spring,spring-boot,spring-integration,spring-amqp,Java,Spring,Spring Boot,Spring Integration,Spring Amqp,我们注意到,当向Spring集成端点(从RabbitMQ)接收到错误消息时,不会重试。如果我们的业务代码(即,接收消息的“服务方法”)中存在问题,从而引发异常,则会按照预期进行重试 这是我们的配置: var myService=。。。 IntegrationFlows.from(Amqp.inboundAdapter(connectionFactory,queueName) .id(集成流id) .AutoStart(自动启动) .configureContainer(c->c.acknowle

我们注意到,当向Spring集成端点(从RabbitMQ)接收到错误消息时,不会重试。如果我们的业务代码(即,接收消息的“服务方法”)中存在问题,从而引发异常,则会按照预期进行重试

这是我们的配置:

var myService=。。。
IntegrationFlows.from(Amqp.inboundAdapter(connectionFactory,queueName)
.id(集成流id)
.AutoStart(自动启动)
.configureContainer(c->c.acknowledgeMode(手动)
.预取计数(10)
.concurrentConsumers(1)
.maxConcurrentConsumers(3))
.messageConverter(messageConverter))
.合计(……)
.handle(myService,“myMethod”,e->e.advice(myAdvice())
.get();
myAdvice
方法的实现方式如下:

ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(200L);
backOffPolicy.setMultiplier(2);
backOffPolicy.setMaxInterval(5000L);

RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy((new SimpleRetryPolicy(MAX_VALUE)));
retryTemplate.setBackOffPolicy(backOffPolicy);
retryTemplate.registerListener(new RetryListenerSupport() {
    @Override
    public <T, E extends Throwable> void onError(RetryContext ctx, RetryCallback<T, E> callback, Throwable e) {
        log.error("Caught {} due to {} (count = {})", e.getClass().getSimpleName(), e.getMessage(), ctx.getRetryCount(), e);
    }
});
StatelessRetryOperationsInterceptorFactoryBean bean = new StatelessRetryOperationsInterceptorFactoryBean();
bean.setRetryOperations(retryTemplate);
bean.setMessageRecoverer(new ImmediateRequeueMessageRecoverer());
return bean.getObject();
似乎没有使用
myAdvice()
方法中指定的
ImmediateRequeueMessageRecoverer
,而是使用了默认的
AmqpRejectAndDontRequeueException
。在我看来,原因很可能是Spring基础设施尚未调用
myAdvice()
方法。我已尝试在
configureContainer
中找到切换消息恢复程序的方法,但似乎找不到这样做的方法

有人知道我如何在spring integration调用“服务方法”之前重新排队/重试失败的消息吗


我们使用的是Spring Integration 5.4.6和Spring Boot 2.4.6。

在创建消息之前执行转换

转换错误通常被认为是致命的-重试没有意义,因为它将再次失败

向入站适配器添加
.errorChannel
;其下游流将获得一条关于转换错误的
ErrorMessage

但是,它也将从下游流中获取错误消息,因此您必须在那里处理所有错误类型

编辑

您可以添加错误通道并在其流上处理转换异常。不过,请记住,消息将一次又一次地重新传递,不会有任何延迟

@springboot应用程序
公共类SO67801807应用程序{
公共静态void main(字符串[]args){
SpringApplication.run(So67801807Application.class,args);
}
@豆子
集成流量(连接工厂cf){
返回IntegrationFlows.from(Amqp.inboundAdapter(cf,“foo”)
.messageConverter(新MC())
.errorChannel(“错误”))
.句柄(…)
.get();
}
@豆子
集成流errorFlow(){
返回IntegrationFlows.from(“错误”)
.handle(msg->{
如果(((ErrorMessage)msg.getPayload().getCause()实例MessageConversionException){
抛出新的ImmediateRequeueAmqpException(“由于转换而重新排队”);
}
否则{
//处理下游流引发的其他一些异常
}
})
.get();
}
}
类MC实现MessageConverter{
@凌驾
公共消息toMessage(对象对象,MessageProperties MessageProperties)抛出MessageConversionException{
返回null;
}
@凌驾
来自消息的公共对象(消息消息)引发MessageConversionException{
抛出新的MessageConversionException(“测试”);
}
}
或者,您可以将自定义错误处理程序添加到容器中。默认错误处理程序认为转换异常是致命的


为了让我正确理解,没有办法只重新询问信息?您是对的,我们现在无法处理此消息,但对我们来说,当一个系统(A)发布(B)感兴趣的消息的更新版本时,会发生此错误,但需要先对其进行更新,然后才能正确读取。也就是说,在A发布更新消息之前,我们忘记了更新B。我们要做的是让B重新调用消息,以便我们可以更新它以支持新消息,然后重新部署。然后可以再次正确处理该消息。我们不想失去味精!当没有
.errorChannel
时,将
消息转换异常
抛出到容器中;容器的默认
ErrorHandler
(a
ConditionalRejectingErrorHandler
)将转换异常视为致命异常,并抛出
AmqpRejectAndDontRequeue
异常。您可以通过配置
FatalExceptionStrategy
更改容器的
ErrorHandler
,也可以向错误通道流添加逻辑。我将编辑我的答案以显示后者的示例。但是请记住,在问题得到解决之前,消息将被一次又一次地重新传递。
[my-service-97c696799-6xs26] org.springframework.amqp.AmqpRejectAndDontRequeueException: Error Handler converted exception to fatal
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler.handleError(ConditionalRejectingErrorHandler.java:146)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeErrorHandler(AbstractMessageListenerContainer.java:1436)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.handleListenerException(AbstractMessageListenerContainer.java:1720)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1495)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:967)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:913)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1288)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1194)
[my-service-97c696799-6xs26]    at java.base/java.lang.Thread.run(Thread.java:831)
[my-service-97c696799-6xs26] Caused by: org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Listener threw exception
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1746)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1636)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1551)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1539)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1530)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1474)
[my-service-97c696799-6xs26]    ... 6 common frames omitted
[my-service-97c696799-6xs26] Caused by: org.springframework.amqp.support.converter.MessageConversionException: Don't know how to convert (Body:'{ "yo" : "MTV Raps" }' MessageProperties [headers={content_type=application/json}, contentType=application/json, contentLength=0, receivedDeliveryMode=NON_PERSISTENT, redelivered=false, receivedExchange=, receivedRoutingKey=myservice.routingkey, deliveryTag=1, consumerTag=amq.ctag-9De2w0uuQxnve_9k6HZ7tw, consumerQueue=myservice.myqueue]) to an object because no event type was found
[my-service-97c696799-6xs26]    at com.mycompany.RabbitMQEventMessageConverter.fromMessage(RabbitMQEventMessageConverter.java:47)
[my-service-97c696799-6xs26]    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.convertPayload(AmqpInboundChannelAdapter.java:361)
[my-service-97c696799-6xs26]    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.createMessageFromAmqp(AmqpInboundChannelAdapter.java:342)
[my-service-97c696799-6xs26]    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.createAndSend(AmqpInboundChannelAdapter.java:334)
[my-service-97c696799-6xs26]    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.onMessage(AmqpInboundChannelAdapter.java:299)
[my-service-97c696799-6xs26]    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1632)
[my-service-97c696799-6xs26]    ... 10 common frames omitted