前言
延迟消费在实际开发中有很多的应用场景,例如:一条消息,需要在30分钟后消费,这个时候就可以使用消息的TTL属性实现延迟消费。具体的实现思路:
生产者发送消息 -----> 交换机 ------> 路由到消息队列(设置消息的TTL属性,表示消息失效后路由到死信交换机) ----> 死信交换机 ----> 路由到死信队列,从死信队列中消费
具体代码
生产者和消费者代码(可以写在同一类中,也可以分开)
/**
* 消息生产者.
* 消息通过正常交换机路由到正常队列,在正常队列中设置消息TTL最大存活时间,超过时间后,消息进入死信交
* 换机,死信交换机将消息路由到死信队列,死信队列也就是实际消费队列,
* 死消费者从实际消费队列中消费消息.
*/
@Component
@Slf4j
public class CustomerProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 正常交换机.
*/
public static final String NORMAL_EXCHANGE = "shop.pay.normal.exchange";
/**
* 死信交换机.
*/
public static final String DL_EXCHANGE = "shop.pay.dl.exchange";
/**
* 正常队列.
*/
public static final String NORMAL_QUEUE = "shop.pay.normal.queue";
/**
* 死信队列.
*/
public static final String DL_QUEUE = "shop.pay.dl.queue";
/**
* 初始化交换机和队列.
*/
@Bean
public DirectExchange normalExchange() {
return new DirectExchange(NORMAL_EXCHANGE);
}
@Bean
public FanoutExchange dlExchange() {
return new FanoutExchange(DL_EXCHANGE);
}
@Bean
public Queue normalQueue() {
Map<String,Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", DL_EXCHANGE);
args.put("x-dead-letter-routing-key", DL_QUEUE);
// 参数解释:durable:是否持久化,exclusive:仅创建者可以使用的私有队列,断开后自动删除
// autoDelete:当所有消费客户端断开连接后,是否自动删除队列
return new Queue(NORMAL_QUEUE, true, false, false, args);
}
@Bean
public Queue dlQueue() {
return new Queue(DL_QUEUE,true,false,false);
}
/**
* 队列和交换机绑定.
*/
@Bean
public Binding normalBinding() {
return BindingBuilder.bind(dlQueue()).to(dlExchange());
}
@Bean
public Binding dlBinding() {
return BindingBuilder.bind(normalQueue()).to(normalExchange()).with("KEY1");
}
/**
* 发送消息.
*
*/
public void sendMessage(String message) {
log.info("sendMessage,发送MQ延时消息,message", message);
// 发送消息
try {
// 设置队列的TTL为3000毫秒,如果超过3000毫秒内没有消费者从队列(通过路由键KEY1
// 路由到与NORMAL_EXCHANGE绑定的NORMAL_QUEUE队列中)中消费该消息,消息就会被转
// 发到队列绑定的死信交换机中,可以监听死信队列,从私信队列中获取到超时的消息
MessagePostProcessor processor = message1 -> {
message1.getMessageProperties().setExpiration("3000");
return message1;
};
rabbitTemplate.convertAndSend(NORMAL_EXCHANGE, "KEY1", (Object) message, processor);
} catch (Exception e) {
log.error("消息发送失败", e);
}
}
/**
* 消费者.
* 监听死信队列.
*
* @param msg 消息内容.
*/
@RabbitHandler
@RabbitListener(queues = DL_QUEUE)
public void process(String msg) {
log.info("process,消费端入参,msg={}", msg);
}
}
发送消息代码
@RestController
public class Main {
@Autowired
private CustomerProducer producer;
/**
* 发送消息.
*/
@GetMapping("produce")
public void sendMessage() {
producer.sendMessage("hello world");
}
}