前言
上来先放一张设计图,看这篇文章的前提是一定得写过或者了解这段业务,不然会看不懂,我下面将会给出我的理解,尽量让大家明白
设计思想
@Transactional
@Override
public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
//前面的代码略过,只关注消息队列的入口
注意,这是整个mq系统的入口,调用了wmsFeignService.orderLockStock(lockVo)的时候就向stock.delay.queue这个延时队列(50min)里面添加了锁库存的消息,
50min之后如果过期,此消息将会被自动路由(设计的时候就是这样设计的,是mq自动做的路由,不用写java代码)到死信队列stock.release.stock.queue中
发送的类型是StockLockedTo对象,意味着这条消息将会被OrderCloseListener的第一个监听函数监听到,将会去解锁库存
下面那个rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder())
是创建订单,订单将会在延时队列放30min直到付款或者取消
如果取消或者30min后,这个消息将被自动路由(设计的时候就是这样设计的,是mq自动做的路由,不用写java代码)到死信队列order.release.order.queue中
这个时候OrderCloseListener是监听着order.release.order.queue的,于是它将调用本类中的closeOrder()方法并将订单关闭这个消息路由到
死信队列stock.release.stock.queue中,传输的是一个OrderTo对象,条消息将会被OrderCloseListener的第二个监听器接收到
所以正确是玩法是:首先用户下好订单,如果在30min内没有支付或者取消了订单,那么证明这个订单是废单,因此需要把锁了的库存给解锁了,30min一到,
OrderCloseListener的第二个监听器将会去解锁订单,50min后,还会有一道保险,也就是第一个监听器监听到50min过期的消息,尝试去解锁订单,形成双保险自动解锁。
R r = wmsFeignService.orderLockStock(lockVo);
if (r.getCode() == 0) {
//锁定成功
response.setOrder(order.getOrder());
//TODO 订单创建成功,发送消息给MQ
/**
* 这是往时限为1分钟的延迟队列传输的信息,里面放着OrderEntity对象,一分钟后被送往死信队列order.release.order.queue
**/
rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());
//删除购物车里的数据
redisTemplate.delete(CART_PREFIX+memberResponseVo.getId());
return response;
} else {
//锁定失败
String msg = (String) r.get("msg");
throw new NoStockException(msg);
}
//后面的代码略过
}
大家对着我上面给出的一大段中文注释,自己去看看是不是这样的
我发现的细节
还有一个细节,如果有两个监听器一起监听一个队列,例如本项目:
@Slf4j
@RabbitListener(queues = "stock.release.stock.queue")
@Service
public class StockReleaseListener {
@Autowired
private WareSkuService wareSkuService;
@RabbitHandler
public void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {
log.info("******50min了,我怕你20min前出问题没解锁到,这是第二道保险******");
//具体代码
}
@RabbitHandler
public void handleOrderCloseRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {
log.info("******30min了,你订单还没支付或者已经取消,我去解锁库存了******");
//具体代码
}
}
上面这两个代码我再次标明了这两个监听器的先后顺序以及作用,如果还懵的现在应该大概明白了吧
我一开始是不知道到底是哪个会先接受消息,后面发现这两个监听器不是去抢同一个消息的,而是
去拿自己对应的消息的,此话怎讲?
例如第一个监听器,它的第一个参数是StockLockedTo to,意味着如果当时给这个消息队列存的对象是StockLockedTo的话,将被这个监听器收到;
第二个监听器,它的第一个参数是OrderTo orderTo,意味着如果当时给这个消息队列存的对象是OrderTo 的话,将被这个监听器收到。
无论有几个监听器,他们只会取到他们应该取到的消息,所以放消息的时候要放好,取出来的时候也得取好,别放进去一个类,取出来用另一个类取,肯定是取不到的,会报错。