RabbitMQ死信队列
死信队列介绍
当一个消息在一个队列中变成死信之后,它能被重新自动推送到另一个交换机——死信交换机,我们声明一个死信队列,和私信交换机进行绑定之后,死信就会被路由到死信队列。
消息变为死信有以下几种情况:
- 消息被否定确认,使用
channel.basicNack或channel.basicReject,并且此时requeue被设置为false。 - 消息在队列的存活时间超过设置的TTL。
- 消息队列的消息数量超过最大长度。
死信队列的目的是防止消息丢失,处理消费过程中的异常情况。
死信队列演示
下面演示消息被否定的情况。
-
配置
rabbitmq: addresses: localhost:5672 username: root password: root virtual-host: / listener: simple: # 被拒绝的消息不会重新放回队列 default-requeue-rejected: false # 消费者需手动确认消息 acknowledge-mode: manual -
在Configuration中声明queue、exchange并绑定
private String bizExchange = "biz.direct.exchange"; private String bizQueue = "biz.queue"; private String routingKey = "key1"; private String dlExchange = "dl.exchange"; private String dlQueue = "dl.queue"; // 声明业务queue,设置x-dead-letter-exchange参数,指定dlx // 本例中dlx是fanout类型的,不用设置x-dead-letter-routing-key @Bean public Queue bizQueue() { return new Queue(bizQueue, true, false, false, Collections.singletonMap("x-dead-letter-exchange", dlExchange)); } // 声明业务exchange @Bean public DirectExchange bizExchange() { return new DirectExchange(bizExchange, true, false); } // 声明绑定关系 @Bean public Binding bizBinding(Queue bizQueue, DirectExchange bizExchange) { return BindingBuilder.bind(bizQueue).to(bizExchange).with(routingKey); } // 声明dead-letter-queue,dlq就是一个普通的队列 @Bean public Queue dlQueue() { return new Queue(dlQueue, true, false, false); } // 声明dead-letter-exchange,dlx可以是任何类型的,这里是fanout类型 @Bean public FanoutExchange dlExchange() { return new FanoutExchange(dlExchange, true, false); } // 声明dlq和dlx绑定关系 @Bean public Binding dlBinding(Queue dlQueue, FanoutExchange dlExchange) { return BindingBuilder.bind(dlQueue).to(dlExchange); } -
消费者直接nack
@RabbitListener(queues = "biz.queue") public void handle(Message message, Channel channel) throws IOException { channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false); } -
生产者
public static void main(String[] args) throws InterruptedException { ApplicationContext context = SpringApplication.run(Main.class, args); RabbitTemplate template = context.getBean(RabbitTemplate.class); template.convertAndSend("biz.direct.exchange", "key1", "abc"); } -
启动后在RabbitMQ管理页面上可以看到,消息最终被路由进了dlQueue
消息过期
设置消息的TTL有两种方式,第一种是直接对消息本身进行单独设置(expiration),每条消息的TTL可以不同;第二种是通过队列属性设置队列中所有消息的过期时间(x-message-ttl)。两种方式一起使用时,消息的TTL以两者中较小的数值为准。
-
设置消息单独的TTL
消息属性带上
expiration:MessageProperties messageProperties = new MessageProperties(); messageProperties.setExpiration(String.valueOf(2000)); // 2000ms template.send("biz.direct.exchange", "key1", new Message("abcd".getBytes(), messageProperties)); -
设置队列中消息的TTL
创建队列时,带上
x-message-ttl参数:new Queue("test.queue", false, false, true, Collections.singletonMap("x-message-ttl", Integer.valueOf(2000))); // 2000ms
队列长度限制
队列长度限制有两种方式,第一种是对队列中消息的条数进行限制x-max-length;第二种是对消息的总字节量进行限制x-max-length-bytes,只计算消息体,不包括消息header、properties。
同样是在创建或声明队列的时候指定参数。