前言
上来先放一张设计图,看这篇文章的前提是一定得写过或者了解这段业务,不然会看不懂,我下面将会给出我的理解,尽量让大家明白
设计思想
@Transactional@Overridepublic 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 {@Autowiredprivate WareSkuService wareSkuService;@RabbitHandlerpublic void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {log.info("******50min了,我怕你20min前出问题没解锁到,这是第二道保险******");//具体代码}@RabbitHandlerpublic void handleOrderCloseRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {log.info("******30min了,你订单还没支付或者已经取消,我去解锁库存了******");//具体代码}}
上面这两个代码我再次标明了这两个监听器的先后顺序以及作用,如果还懵的现在应该大概明白了吧
我一开始是不知道到底是哪个会先接受消息,后面发现这两个监听器不是去抢同一个消息的,而是
去拿自己对应的消息的,此话怎讲?
例如第一个监听器,它的第一个参数是StockLockedTo to,意味着如果当时给这个消息队列存的对象是StockLockedTo的话,将被这个监听器收到;
第二个监听器,它的第一个参数是OrderTo orderTo,意味着如果当时给这个消息队列存的对象是OrderTo 的话,将被这个监听器收到。
无论有几个监听器,他们只会取到他们应该取到的消息,所以放消息的时候要放好,取出来的时候也得取好,别放进去一个类,取出来用另一个类取,肯定是取不到的,会报错。