什么是死信?
当一条消息出现以下情况,即可称为死信:
- 消息被拒绝(basicNack/basicReject),同时消息不能重写进入队列(requeue=false)
- 消息 x-message-ttl 过期
- 队列达到最大长度,消息
什么是死信队列?
消费死信消息的队列(通过死信交换机,死信队列对死信消息进行路由,消费)
死信队列可以做什么?
- 如订单支付超时取消问题
- 延时处理问题
- 防止消息堆积
死信队列如何实现?
- 创建工作交换机,工作队列,绑定关系
- 创建工作队列对应的死信交换机,死信队列,绑定关系
- 绑定工作队列与死信队列,设置工作队列的TTL
- 创建工作队列的监听
- 创建死信队列的监听
消息队列全局配置
spring:
rabbitmq:
host: localhost
port: 5671
username: gg
password: gg
listener:
direct:
acknowledge-mode: manual
simple:
acknowledge-mode: manual
publisher-confirms: true
cache:
channel:
size: 100
connection:
size: 100
mode: connection
template:
retry:
enabled: true
max-attempts: 10
创建工作队列,死信队列并绑定对应关系
/**
* 死信队列,延时队列创建
* @author flj
*/
@Configuration
public class RabbitmqConfig {
/******************************************公有配置***************************************/
/**
* 绑定死信交换机key(绑定死信队列固定参数)
*/
private final String xDeadLetterExchange = "x-dead-letter-exchange";
/**
* 绑定死信路由key(绑定死信队列固定参数)
*/
private final String xDeadLetterRoutingKey = "x-dead-letter-routing-key";
/**
* 超时时间(配置延时队列固定参数)
*/
private final String xMessageTtl = "x-message-ttl";
/******************************************* 业务配置*********************************** */
/**
* 业务交换机
*/
public static final String WORK_EXCHANGE = "work_exchange";
/**
* 业务routing_key
*/
public static final String WORK_ROUTING_KEY = "work_routing_key";
/**
* 死信交换机
*/
public static final String DEAD_LETTER_EXCHANGE= "dead_letter_exchange";
/**
* 死信路由
*/
public static final String DEAD_LETTER_ROUTING_KEY = "dead_routing_key";
/**
* 死信队列
*/
public static final String DEAD_LETTER_QUEUE = "dead_letter_queue";
/*****************************************创建死信交换机,死信队列,绑定关系************************/
/**
* 创建死信交换机
*/
@Bean("deadLetterExchange")
public DirectExchange deadLetterExchange(){
return new DirectExchange(DEAD_LETTER_EXCHANGE);
}
/**
* 创建死信队列
*/
@Bean("deadLetterQueue")
public Queue deadLetterQueue(){
return new Queue(DEAD_LETTER_QUEUE);
}
/**
* 死信队列,绑定死信交换机,死信路由
* @param queue 死信队列
* @param exchange 绑定死信交换机
* @return Binding
*/
@Bean
public Binding deadBinding(@Qualifier("deadLetterQueue") Queue queue, @Qualifier("deadLetterExchange") DirectExchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(DEAD_LETTER_ROUTING_KEY);
}
/*****************************************创建工作交换机,工作队列(绑定死信队列),绑定关系****************************/
/**
* 创建第三方交换机
* @return
*/
@Bean("workExchange")
public DirectExchange workExchange(){
return new DirectExchange(WORK_EXCHANGE);
}
/**
* 创建三方工单队列
* 绑定死信交换机key: x-dead-letter-exchange
* 绑定死信路由key: x-dead-letter-routing-key
* @return
*/
@Bean("workQueue")
public Queue workQueue(){
Map<String, Object> args = new HashMap<>(3);
args.put(xDeadLetterExchange, DEAD_LETTER_EXCHANGE);
args.put(xDeadLetterRoutingKey, DEAD_LETTER_ROUTING_KEY);
args.put(xMessageTtl, 60000);
return QueueBuilder.durable(WORK_QUEUE).withArguments(args).build();
}
/**
* 绑定队列,交换机,路由
* @param queue 队列
* @param exchange 交换机
* @return Binding
*/
@Bean
public Binding workBinding(@Qualifier("workQueue") Queue queue, @Qualifier("workExchange") DirectExchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(WORK_ROUTING_KEY);
}
}
创建消息监听
/**
* 消息消费
* @author flj
*/
@Slf4j
@Component
public class WorkQueueConsumer {
/**
* 监听工作队列
* @param message 消息
* @param channel 通道
* @throws Exception
*/
@RabbitListener(queues = RabbitmqConfig.WORK_QUEUE)
public void messageConsumer(Message message, Channel channel) throws Exception{
log.debug("打印接收消息{}", new String(message.getBody()));
// 拒绝当前消息,进入绑定的死信队列
// 参数1:消息标签;参数2:仅拒绝当前消息;参数3:不重新进入队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
/**
* 监听与工作队列绑定的死信队列
* @param message 消息
* @param channel 通道
* @throws IOException
*/
@RabbitListener(queues = RabbitmqConfig.DEAD_LETTER_QUEUE)
public void deadLetterQueueHandle(Message message, Channel channel) throws IOException {
log.debug("打印死信消息{}", new String(message.getBody()));
// 手动消息确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}