文章内容输出来源:拉勾教育Java高薪训练营;
在淘宝或者京东下单,订单创建成功,等待支付,一般会给30分钟的时间,开始倒计时。如果在这段时间内用户没有支付,则默认订单取消,该如何实现?
用户下单,将订单存放到交换机ex.order(过期时间为下单超时时间30分钟),消息到q_order队列中,不设置该队列的消费者(故此消息一直未消费).,同时指定一个死信交换机ex.order.dlx,并绑定一个死信队列q.order.dlx,当消息超过30分钟过期变成死信时,该消就会被发送到该死信队列上,由死信消费者消费,判断订单id是否支付,如果未支付则修改为支付超时订单过期。
声明订单和死信队列和交换机,并绑定,(订单消息过期时间这里为了方便测试,设置为10s)
@Configuration
public class RabbitConfig {
@Bean
public Queue queue() {
Map<String, Object> props = new HashMap<>();
// 消息的生存时间 10s
props.put("x-message-ttl", 10000);
// 设置该队列所关联的死信交换器(当队列消息到期后依然没有消费,则加入死信 队列)
props.put("x-dead-letter-exchange", "ex.order.dlx");
// 设置该队列所关联的死信交换器的routingKey,如果没有特殊指定,使用原队列的routingKey
props.put("x-dead-letter-routing-key", "order.dlx");
Queue queue = new Queue("q.order", true, false, false, props);
return queue;
}
@Bean
public Queue queueDlx() {
Queue queue = new Queue("q.order.dlx", true, false, false);
return queue;
}
@Bean
public Exchange exchange() {
DirectExchange exchange = new DirectExchange("ex.order", true,false, null);
return exchange;
}
/**
* 死信交换器
* @return
*/
@Bean
public Exchange exchangeDlx() {
DirectExchange exchange = new DirectExchange("ex.order.dlx", true,false, null);
return exchange;
}
@Bean
public Binding binding() {
return BindingBuilder.bind(queue()).to(exchange()).with("order").noargs();
}
@Bean
public Binding bindingDlx() {
return BindingBuilder.bind(queueDlx()).to(exchangeDlx()).with("order.dlx").noargs();
}
}
订单contoller
@RestController
public class OrderController {
@Autowired
private AmqpTemplate rabbitTemplate;
@Autowired
private OrderInfoMapper orderInfoMapper;
/**
* 提交订单
* @return
*/
@RequestMapping("/submit")
public Integer submitOrder() {
//添加订单信息到mysql
OrderInfo orderInfo = new OrderInfo();
orderInfo.setStatus(0);
orderInfoMapper.insertSelective(orderInfo);
//将消息发送到order队列
rabbitTemplate.convertAndSend("ex.order", "order", orderInfo.getId());
return orderInfo.getId();
}
/**
* 支付
* @return
*/
@RequestMapping("/pay/{id}")
public Integer pay(@PathVariable Integer id) {
OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(id);
//订单过期
if(orderInfo.getStatus().equals(2)){
//跳转用户历史账单中查看因付款超时而取消的订单
return 2;
}else{
orderInfo.setStatus(1);
orderInfoMapper.updateByPrimaryKey(orderInfo);
//跳转到付款成功页面
return 1;
}
}
@RequestMapping("/orderList")
public List<OrderInfo> orderList() {
List<OrderInfo> orderInfos = orderInfoMapper.selectAll();
return orderInfos;
}
}
死信消费者监听q.order.dlx死信队列,消费过期消息
@Component
public class TimeoutOrderListener {
@Autowired
OrderInfoMapper orderInfoMapper;
@RabbitListener(queues = "q.order.dlx")
public void onMessage(String orderId) {
//通过从死信队列中获取的订单号,查询订单
OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(Integer.valueOf(orderId));
//判断订单状态如果为未支付,则修改状态为过期
if(orderInfo.getStatus().equals(0)) {
orderInfo.setStatus(2);
orderInfoMapper.updateByPrimaryKey(orderInfo);
}
}
}
提交订单
页面跳转至支付
观察订单信息进入到订单队列
10s之后由于一直没有点支付,订单队列中的消息一直未被消费,消息过期,进入到q.order.dlx死信队列,死信队列消费者消费到消息,修改订单状态为过期(由于消费者直接监听到进行消费,所以可能看不到死信队列消息)
此时点击支付后,由于订单已经过期,直接跳转至过期
https://github.com/Nicococococo/rabbitmq-order
工作几年,一直都没有去体系化的学习,很多东西没有复杂的工作场景经验,去年综合几家机构,最后还是决定报了拉勾的高薪训练营,在这里也是实实在在的学习到了很多,学完掌握程度也比之前深了很多,而且还有定期的内推,多了更多的机会,真的对我有了很大的帮助提升。