实际工作中,有这样一种需求,订单1分钟未支付直接关闭订单,这时可以考虑用rabbitMQ的延时队列
大概思路:订单创建完成后生产者发送消息到mq延时队列中,一分钟后消息变为死信转发到正常队列中,
而消费者则一直监听正常队列
项目集成mq:
1>导入依赖:
<!--mq依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2>yml:(mq安装步骤度娘上很多)
spring:
rabbitmq:
host: 192.168.0.161
port: 5672
username: admin
password: admin123
3>mq配置类:(定义正常队列和延时队列的 消息,交换机,route-key以及他们之间的绑定关系)
@Configuration
public class AMQPConfig {
private static final Logger logger = LoggerFactory.getLogger(AMQPConfig.class);
// /**
// * 消息队列(测试)
// */
// @Bean
// public Queue testConfig() {
// logger.info("=============打开AMQP-"+ AMQPConstant.QUEUES_TEST_CONFIG);
// return new Queue(AMQPConstant.QUEUES_TEST_CONFIG);
// }
/**
* 正常队列(即消息过期后由延时队列自动转发到的正常队列)
*/
//正常队列名称常量设置
public static final String ORDER_QUEUE_NAME = "tao.order.queue";
public static final String ORDER_EXCHANGE_NAME = "tao.order.exchange";
public static final String ORDER_ROUTING_KEY = "order";
//定义正常queue,Exchange,route-key之间的绑定关系
@Bean
public Queue orderQueue() {
return new Queue(ORDER_QUEUE_NAME, true);
}
@Bean
public TopicExchange orderTopicExchange() {
return new TopicExchange(ORDER_EXCHANGE_NAME);
}
@Bean
public Binding orderBinding() {
// 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
return BindingBuilder.bind(orderQueue()).to(orderTopicExchange()).with(ORDER_ROUTING_KEY);
}
/**
* 延时队列
*/
//延时队列名称常量相关设置
private static final String ORDER_DELAY_QUEUE = "tao.order.delay.queue";
public static final String ORDER_DELAY_EXCHANGE = "tao.order.delay.exchange";
public static final String ORDER_DELAY_ROUTING_KEY = "order_delay";
//定义延时queue,Exchange,route-key之间的绑定关系
//并且设置消息过期后的转发的死信队列以及route-key
@Bean
public Queue delayOrderQueue() {
Map<String, Object> params = new HashMap<>();
// x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
params.put("x-dead-letter-exchange", ORDER_EXCHANGE_NAME);
// x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
params.put("x-dead-letter-routing-key", ORDER_ROUTING_KEY);
return new Queue(ORDER_DELAY_QUEUE, true, false, false, params);
}
@Bean
public DirectExchange orderDelayExchange() {
return new DirectExchange(ORDER_DELAY_EXCHANGE);
}
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(delayOrderQueue()).to(orderDelayExchange()).with(ORDER_DELAY_ROUTING_KEY);
}
}
4>生产者类:
@Component
@Slf4j
public class DelaySender {
@Autowired
private AmqpTemplate amqpTemplate;
public void sendDelay(Order order) {
log.info("==========发送延时消息-----------【订单生成时间】" + new Date().toString() + order.toString() );
this.amqpTemplate.convertAndSend(AMQPConfig.ORDER_DELAY_EXCHANGE, AMQPConfig.ORDER_DELAY_ROUTING_KEY, order, message -> {
// 如果配置了 params.put("x-message-ttl", 5 * 1000); 那么这一句也可以省略,具体根据业务需要是声明 Queue 的时候就指定好延迟时间还是在发送自己控制时间
message.getMessageProperties().setExpiration(1 * 1000 * 60 + "");
return message;
});
}
public void sendNow(Order order) {
log.info("==========发送实时消息-----------【订单生成时间】" + new Date().toString() + order.toString() );
this.amqpTemplate.convertAndSend(AMQPConfig.ORDER_EXCHANGE_NAME, AMQPConfig.ORDER_ROUTING_KEY,order);
}
}
5>消费者类:
@Component
@Slf4j
public class DelayReceiver {
@RabbitListener(queues = {AMQPConfig.ORDER_QUEUE_NAME})
public void orderDelayQueue(Order order, Message message, Channel channel) {
log.info("###########################################");
log.info("【orderDelayQueue 监听的消息】 - 【消费时间】 - [{}]- 【订单内容】 - [{}]", new Date(), order.toString());
if(order.getOrderStatus() == 0) {
order.setOrderStatus(2);
log.info("【该订单未支付,取消订单】" + order.toString());
} else if(order.getOrderStatus() == 1) {
log.info("【该订单已完成支付】");
} else if(order.getOrderStatus() == 2) {
log.info("【该订单已取消】");
}
log.info("###########################################");
}
}
6>测试类:
@Api(value="AmqpController",description="amqp测试",tags={"amqp测试"})
@RestController
@RequestMapping("amqp")
public class TestAmqpContorller {
@Autowired
private DelaySender delaySender;
@GetMapping("/sendDelay")
public Object sendDelay() {
Order order1 = new Order();
order1.setOrderStatus(0);
order1.setOrderId("123456");
order1.setOrderName("小米6");
//测试mq发送实时消息
delaySender.sendNow(order1);
//测试mq发送延时消息
delaySender.sendDelay(order1);
return "ok";
}
}
访问:http://192.168.0.161:9002/amqp/sendDelay
控制台:
c.he.hsdyc.common.log.GlobalInterceptor : -------处理开始START------
c.he.hsdyc.common.log.GlobalInterceptor : 请求Controller: < com.he.hsdyc.controller.TestAmqpContorller >
c.he.hsdyc.common.log.GlobalInterceptor : 请求方法: < sendDelay >
c.he.hsdyc.common.log.GlobalInterceptor : 请求参数:{}
c.he.hsdyc.common.log.GlobalInterceptor : 请求方式:GET
c.he.hsdyc.common.log.GlobalInterceptor : 请求路径:http://192.168.0.161:9002/amqp/sendDelay
com.he.hsdyc.config.Queue.DelaySender : ==========发送实时消息-----------【订单生成时间】Tue Aug 13 09:17:43 CST 2019Order(orderId=123456, orderStatus=0, orderName=小米6)
com.he.hsdyc.config.Queue.DelaySender : ==========发送延时消息-----------【订单生成时间】Tue Aug 13 09:17:43 CST 2019Order(orderId=123456, orderStatus=0, orderName=小米6)
com.he.hsdyc.config.Queue.DelayReceiver : ###########################################
com.he.hsdyc.config.Queue.DelayReceiver : 【orderDelayQueue 监听的消息】 - 【消费时间】 - [Tue Aug 13 09:17:43 CST 2019]- 【订单内容】 - [Order(orderId=123456, orderStatus=0, orderName=小米6)]
com.he.hsdyc.config.Queue.DelayReceiver : 【该订单未支付,取消订单】Order(orderId=123456, orderStatus=2, orderName=小米6)
com.he.hsdyc.config.Queue.DelayReceiver : ###########################################
c.he.hsdyc.common.log.GlobalInterceptor : -------处理完成END------
com.he.hsdyc.config.Queue.DelayReceiver : ###########################################
com.he.hsdyc.config.Queue.DelayReceiver : 【orderDelayQueue 监听的消息】 - 【消费时间】 - [Tue Aug 13 09:18:43 CST 2019]- 【订单内容】 - [Order(orderId=123456, orderStatus=0, orderName=小米6)]
com.he.hsdyc.config.Queue.DelayReceiver : 【该订单未支付,取消订单】Order(orderId=123456, orderStatus=2, orderName=小米6)
com.he.hsdyc.config.Queue.DelayReceiver : ###########################################
可以看到,同时生产了两个消息,即时消息和延时消息,但是消费者一个是即时收到,另一个是一分钟后收到