Spring(非SpringBoot)中给RabbitMQ死信队列消息追加异常详情
在Spring非Boot环境下给RabbitMQ死信消息追加异常详情头
要实现把异常信息(比如异常消息、堆栈跟踪)加到死信队列的消息头里,咱们得自定义消息的错误处理逻辑——毕竟默认的RabbitMQ死信机制只会传递原消息的基础属性,不会自动附加异常详情。下面是适配你现有配置的具体实现步骤:
1. 自定义错误处理器捕获异常并修改消息头
先写一个RabbitListenerErrorHandler的实现类,它会在消息消费抛出异常时触发,我们可以在这里把异常信息塞进原消息的headers里,之后再让RabbitMQ的死信机制把带新headers的消息转去DLQ:
import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.listener.api.RabbitListenerErrorHandler; import org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException; import org.springframework.stereotype.Component; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; @Component("customRabbitListenerErrorHandler") public class CustomRabbitListenerErrorHandler implements RabbitListenerErrorHandler { @Override public Object handleError(Message message, org.springframework.messaging.Message<?> amqpMessage, ListenerExecutionFailedException exception) throws Exception { // 先拿到原消息的headers,为空就新建一个 Map<String, Object> headers = new HashMap<>(message.getMessageProperties().getHeaders()); // 把异常消息塞进headers headers.put("exception-message", exception.getCause().getMessage()); // 把堆栈跟踪转成字符串也加进去 StringWriter stackTraceWriter = new StringWriter(); exception.getCause().printStackTrace(new PrintWriter(stackTraceWriter)); headers.put("exception-stacktrace", stackTraceWriter.toString()); // 更新原消息的headers message.getMessageProperties().setHeaders(headers); // 重新抛出异常,让RabbitMQ触发原生的死信机制 throw exception; } }
2. 给消息监听器绑定这个错误处理器
在你的@RabbitListener注解里指定刚写的错误处理器,这样消费异常时就会走咱们的自定义逻辑:
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class TestMessageConsumer { @RabbitListener(queues = "test", errorHandler = "customRabbitListenerErrorHandler") public void processTestMessage(String messageContent) { // 这里模拟消息处理时抛出异常 throw new RuntimeException("测试异常:消息处理失败,触发死信"); } }
3. (可选)手动发送死信(更灵活的方案)
如果你不想依赖RabbitMQ原生的死信交换机配置,也可以在错误处理器里手动把带异常头的消息发送到DLQ,这种方式能完全控制死信的内容和流向:
首先给错误处理器注入RabbitTemplate,然后手动发送:
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component("customRabbitListenerErrorHandler") public class CustomRabbitListenerErrorHandler implements RabbitListenerErrorHandler { @Autowired private RabbitTemplate rabbitTemplate; @Override public Object handleError(Message message, org.springframework.messaging.Message<?> amqpMessage, ListenerExecutionFailedException exception) throws Exception { Map<String, Object> headers = new HashMap<>(message.getMessageProperties().getHeaders()); headers.put("exception-message", exception.getCause().getMessage()); StringWriter stackTraceWriter = new StringWriter(); exception.getCause().printStackTrace(new PrintWriter(stackTraceWriter)); headers.put("exception-stacktrace", stackTraceWriter.toString()); message.getMessageProperties().setHeaders(headers); // 手动发送到你的DLQ队列 rabbitTemplate.send("test.dlq", message); // 返回null表示消息已处理,不会触发重试(根据你的重试需求调整) return null; } }
注意:如果用手动发送的方式,建议你去掉原队列
queueTest的x-dead-letter-exchange和x-dead-letter-routing-key配置,避免同一消息被重复发送到DLQ。
验证死信的异常头
最后,你可以写一个DLQ的消费者,打印出消息头里的异常信息,确认是否生效:
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; import java.util.Map; @Component public class DlqMessageConsumer { @RabbitListener(queues = "test.dlq") public void processDlqMessage(String messageContent, @Header Map<String, Object> headers) { System.out.println("收到死信消息内容:" + messageContent); System.out.println("异常详情:" + headers.get("exception-message")); System.out.println("异常堆栈:" + headers.get("exception-stacktrace")); } }
这样就能在死信消息的headers里拿到完整的异常信息啦。
内容的提问来源于stack exchange,提问作者TOUDIdel




