Dead Letter Exchange
在队列上指定一个Exchange,则在该队列上发生如下情况,
1.消息被拒绝(basic.reject or basic.nack),且requeue=false
2.消息过期而被删除(TTL)
3.消息数量超过队列最大限制而被删除
4.消息总大小超过队列最大限制而被删除
就会把该消息转发到指定的这个exchange
同时也可以指定一个可选的x-dead-letter-routing-key
,表示默认的routing-key
,如果没有指定,则使用消息的routeing-key
(也跟指定的exchange有关,
如果是Fanout类型的exchange
,则会转发到所有绑定到该exchange
的所有队列)。
拒绝消息或者nack
示列
定义一个队列zhihao.miao.order
,其有属性x-dead-letter-exchange
是zhihao.miao.exchange.pay
,往Exchange名为zhihao.miao.exchange.order
中发送消息。
往zhihao.miao.order
队列中发送消息,
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import java.util.concurrent.TimeUnit;
@ComponentScan
public class Application {
public static void main(String[] args) throws Exception{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
byte[] body = "hello,zhihao.miao".getBytes();
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("json");
Message message = new Message(body,messageProperties);
rabbitTemplate.send("zhihao.miao.exchange.order","zhihao.miao.order",message);
TimeUnit.SECONDS.sleep(30);
context.close();
}
}
配置类:
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MQConfig {
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setUri("amqp://zhihao.miao:[email protected]:5672");
return factory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
return rabbitAdmin;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
}
此时消息端拒绝消费这个消息
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import java.util.concurrent.TimeUnit;
@ComponentScan
public class Application {
public static void main(String[] args) throws Exception{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
System.out.println(rabbitTemplate);
TimeUnit.SECONDS.sleep(30);
context.close();
}
}
配置类
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MQConfig {
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setUri("amqp://zhihao.miao:[email protected]:5672");
return factory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
return rabbitAdmin;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
@Bean
public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("zhihao.miao.order");
container.setDefaultRequeueRejected(false);
//手动确认
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setMessageListener(new ChannelAwareMessageListener(){
@Override
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println("=====rece msg======");
System.out.println(new String(message.getBody()));
//执行拒绝消息
channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
}
});
return container;
}
}
因为其指定了x-dead-letter-exchange
是zhihao.miao.exchange.pay
,所以会将消息转发到zhihao.miao.exchange.pay
,而因为没有指定x-dead-letter-routing-key
,所以会使用默认的发送的消息的route key(zhihao.miao.order
)进行路由,而我们zhihao.miao.exchange.pay
的路由信息如下,所以会将消息转发到zhihao.miao.auto
队列中去。
示列2
定义了队列zhihao.miao.order
,不仅定义了x-dead-letter-exchange
属性,也指定了x-dead-letter-routing-key
属性
显而易见当拒绝了该消息的时候就会转发到了zhihao.miao.exchange.pay
,而应该该队列指定了route key为zhihao.miao.pay
,所以转发到了zhihao.miao.pay
队列中去了。
代码很上面的一样。
总结
上面的示列展示了当定义队列时指定了x-dead-letter-exchange
(x-dead-letter-routing-key
视情况而定),并且消费端执行拒绝策略的时候将消息路由到指定的Exchange中去。我们知道还有二种情况会造成消息转发到死信队列。
一种是消息过期而被删除,可以使用这个方式使的rabbitmq实现延迟队列的作用。还有一种就是消息数量超过队列最大限制而被删除或者消息总大小超过队列最大限制而被删除
参考资料
Dead Letter Exchanges
使用TTL(消息过期)来实现消息延迟