购物车:
1数据结构——Map<String,Map<String,String>>
2临时购物车和登录购物车
订单:
1.订单的创建流程
2.订单的不同状态
3.幂等性处理
4.订单超时后的自动关单和库存自动解锁 (基于 MQ实现,RabbitTemplate)
5.MQ—— 可靠抵达,ACK消息确认机制;
默认ACK,当消息被消费者接收,消息会从broker的queue队列中被移除。
发送端的消息确认机制,有: confirmCallback 和 returnCallback
消费端的消息确认机制,是 ack 确认机制:
可靠抵达,ACK消息确认机制(消费端确认机制):Broker相当于服务器
消费端的消息确认机制,是 ack 确认机制:
如果不开启手动ack确认模式,则会出现,在消费端处理了n个MQ的消息后,突然系统宕机等问题,则会造成数据丢失的问题。
因此要解决该问题,可以设置 手动 ack确认机制。
消费者Consumer手动确认模式,只要我们没有明确的告诉MQ,货物被签收,那么消息将一直处于 unacked状态,此时即使Consumer消费端出现宕机,MQ中的消息也不会丢失,会重新变成 Ready状态,下次有新的Consumer进来,MQ就会将消息发送给该Consumer。
具体的进行手动ack,逻辑代码如下:
6.Rabbit MQ延时队列,实现 定时任务
应用场景: 未付款的订单,超过一定时间后,系统自动取消订单并释放占有的物品。
常用的解决方案:
Spring的schedule定时任务轮询数据库;
缺点: 消耗系统内存、增加了数据库的压力、存在较大的时间误差。
解决方案: RabbitMQ的消息TTL和死信 Exchange结合。
消息的TTL就是消息的存活时间:
RabbitMQ可以对 队列 和 消息 分别设置TTL。
·对队列设置就是: 队列没有消费者 连接情况下的 保留时间, 也可以对每一个单独的消息设置TTL。 如果 使劲按超过了TTL,则认为消息就死了,称之为 死信。
·如果队列和消息 都设置了TTL,则取较小的为准。因此,如果一个消息被路由到不同的队列中,其存活时间是不一样的(不同的队列设置的存活时间是不一样的)。
具体地: 在MQ中过期的消息 设置为 不被服务器丢弃,将这些过期了的消息放入到一个交换机中(死信交换机Exchange),并在该交换机后边再接入一个队列Queue(死信队列),其它的服务需要监听的是该死信队列,由此结合两者可以实现一个 延时队列。
1.设置队列过期时间(推荐,可以不需要根据消息的过期时间,批量拿出数据丢到死信Exchange中)
2.设置消息过期时间
自动解锁库存的逻辑:
关单的逻辑:
当创建的订单在一定的时间内(队列的存活时间)未进行支付,则需要先进行关闭订单,此时就会给库存服务发送解锁库存的消息,进行解锁库存。
上图中第2种模式,会出现问题:在创建订单时——库存锁定信息也随即创建,(由于出现机器卡顿,消息延迟等问题,导致订单的状态信息发送太慢),库存解锁的时间已到(库存和订单MQ是两个交换机Exchange),则库存将解锁, 然后库存解锁完毕。此时订单的状态才刚刚到达库存服务,库存服务发现订单状态是新建状态,则不进行库存解锁,由于订单创建时,订单创建成功的信息和库存锁定的信息一起创建(个人理解:1+1的消息,并没有多的任何一个消息)那么库存将再也收不到库存解锁的消息,导致卡顿的订单锁定的库存永远无法得到释放。
最终整个的 消息队列实现库存解锁的逻辑为:
在进行消息的传递过程中,可能会出现的问题:
注: 自动ACK的状态下。消费者收到消息,但还没来得及消费然后宕机。