Java 具有延迟的死信队列的处理

Java 具有延迟的死信队列的处理,java,spring-boot,rabbitmq,spring-cloud-stream,dead-letter,Java,Spring Boot,Rabbitmq,Spring Cloud Stream,Dead Letter,我希望执行以下操作:当消息失败并落在死信队列中时,我希望等待5分钟并在队列中重新发布相同的消息 今天,使用Spring Cloud Streams和RabbitMQ,我完成了以下代码: @组件 公共类HandlerDlq{ 私有静态最终记录器Logger=LoggerFactory.getLogger(HandlerDlq.class); 私有静态最终字符串X_RETRIES_HEADER=“X-RETRIES”; 私有静态最终字符串X_DELAY_HEADER=“X-DELAY”; 私有静态最

我希望执行以下操作:当消息失败并落在死信队列中时,我希望等待5分钟并在队列中重新发布相同的消息

今天,使用Spring Cloud Streams和RabbitMQ,我完成了以下代码:

@组件
公共类HandlerDlq{
私有静态最终记录器Logger=LoggerFactory.getLogger(HandlerDlq.class);
私有静态最终字符串X_RETRIES_HEADER=“X-RETRIES”;
私有静态最终字符串X_DELAY_HEADER=“X-DELAY”;
私有静态最终整数重试次数=3;
专用静态最终整数延迟\u MS=300000;
私人兔样板兔样板;
@自动连线
公共HandlerDlq(RabbitTemplate RabbitTemplate){
this.rabbitmplate=rabbitmplate;
}
@RabbitListener(队列=MessageInputProcessor.DLQ)
公共无效重新发布(消息失败消息){
映射头=failedMessage.getMessageProperties().getHeaders();
Integer retriesHeader=(整数)头.get(X\u RETRIES\u头);
if(retriesHeader==null){
检索器=0;
}
if(重试器>重试次数){
warn(“消息{}添加到失败消息队列”,failedMessage);
this.rabbitmplate.send(MessageInputProcessor.FAILED,failedMessage);
抛出新的ImmediateAcknowledgeAmqpException(“消息在“+重试次数+尝试次数”之后失败”);
}
检索器++;
headers.put(X_RETRIES_HEADER,retriesHeader);
headers.put(X_DELAY_HEADER,DELAY_MS*retriesHeader);
warn(“重试消息,{}次尝试”,retriesHeader);
this.rabbitTemplate.send(MessageInputProcessor.DELAY\u交换,MessageInputProcessor.INPUT\u目的地,failedMessage);
}
@豆子
公共DirectExchange delayExchange(){
DirectExchange=新的DirectExchange(MessageInputProcessor.DELAY\u exchange);
exchange.setDelayed(true);
换汇;
}
@豆子
公共绑定bindOriginalToDelay(){
将BindingBuilder.bind(新队列(MessageInputProcessor.INPUT\u DESTINATION)).to(delayExchange()).with(MessageInputProcessor.INPUT\u DESTINATION);
}
@豆子
公共停车场(){
返回新队列(MessageInputProcessor.FAILED);
}
}
我的
MessageInputProcessor
接口:

公共接口消息输入处理器{
String INPUT=“myInput”;
字符串输入\ u DESTINATION=“myInput.group”;
字符串DLQ=INPUT_DESTINATION+“.DLQ”;//来自application.properties文件
字符串失败=输入+“-失败”;
字符串延迟\交换=输入\目的地+“-DlqReRouter”;
@输入
SubscribableChannel storageManagerInput();
@输入(MessageInputProcessor.FAILED)
SubscribableChannel StorageManager失败();
}
和我的属性文件:

#dlx/dlq setup - retry dead letter 5 minutes later (300000ms later)
spring.cloud.stream.rabbit.bindings.myInput.consumer.auto-bind-dlq=true
spring.cloud.stream.rabbit.bindings.myInput.consumer.republish-to-dlq=true
spring.cloud.stream.rabbit.bindings.myInput.consumer.dlq-ttl=3000
spring.cloud.stream.rabbit.bindings.myInput.consumer.delayedExchange=true


#input
spring.cloud.stream.bindings.myInput.destination=myInput
spring.cloud.stream.bindings.myInput.group=group
有了这段代码,我可以从死信队列中读取数据,捕获标题,但我无法将其放回队列(行
LOGGER.warn(“重试消息,{}尝试”,retriesHeader);
只运行一次,即使我的时间非常慢)

我的猜测是,
bindOriginalToDelay
方法将交换绑定到一个新队列,而不是我的。然而,我没有找到一种方法让我的队列绑定到那里,而不是创建一个新的队列。但我甚至不确定这是不是错误

我还尝试发送到
MessageInputProcessor.INPUT
而不是
MessageInputProcessor.INPUT\u DESTINATION
,但它没有按预期工作

另外,不幸的是,由于项目的依赖性,我无法更新Spring框架


一段时间后,你能帮我把失败的消息放回队列吗?我真的不想把
线程放在那里。sleep
那里…

使用该配置,
myInput.group
绑定到延迟(主题)交换
myInput
,并使用路由键

您可能应该删除
spring.cloud.stream.rabbit.bindings.myInput.consumer.delayedExchange=true
,因为您不需要延迟主交换

它还将绑定到显式延迟交换,键为
myInput.group

在我看来,一切都是正确的;您应该看到绑定到两个交换机的相同(单个)队列:

myInput.group.dlq
通过键
myInput.group
绑定到
DLX

您应该设置一个较长的TTL,并检查DLQ中的消息,以查看是否有突出的内容

编辑

我刚刚复制了你的代码,延迟了5秒,对我来说效果很好(在主交换机上关闭了延迟)


也许你认为它不起作用,因为你在主交换上也有延迟?

我刚刚在
myInput
交换上测试了你的代码,延迟5秒,没有延迟,一切正常;也许你被误导是因为主交换的额外延迟?不幸的是不是。我已经检查了绑定,就像您发布的图像一样,在我的代码中它没有绑定到
DlqReRouter
one。事实上,它甚至没有出现在交换列表中。我想知道这是不是因为在我跑步之前队列就已经存在了,或者也许我在某处把东西和名字混在一起了……是的;一旦队列被创建,您就不能更改它;您必须删除它才能使用新参数创建它。
Retrying message, 4 attempts
added to failed messages queue