一、异步秒杀思路
(1)业务优化思路
当执行优惠券秒杀业务时,我们需要去串行执行内部流程的每一步动作,整个业务最终的耗时就是每一步耗时之和,而在流程中有许多动作都涉及到对数据库的读写操作,这就会导致整个业务的执行耗时较长、并发能力弱。
解决办法:把整段业务分为两部分,一部分为秒杀资格的判断,这块动作执行耗时较短;另一部分是减库存下单,这块动作是对数据库的读写操作,耗时较久。我们可以把这两部分业务交给两条线程去完成,也就是主线程去判断用户的购买资格,判断有购买资格后再去开启一个独立线程来处理对数据库的读写操作。同时也可以借助Redis缓存去实现对秒杀资格判断的业务执行效率优化。
(2)如何在Redis中完成库存与一人一单的判断
二、基于Redis完成秒杀资格判断
①保存秒杀优惠券方法改造
②Lua脚本编写
③判断优惠券秒杀资格java代码改造
- 注入Lua脚本
- 改造java代码(阻塞队列的内容将在后续进行补充)
④完善阻塞队列实现异步下单功能
- 注入阻塞队列
BlockingQueue阻塞队列的特点:当一个线程尝试从该队列中获取元素时,如果队列中没有元素,那么该线程就会被阻塞,直到队列中有元素才会被唤醒并获取元素。
- 完善抢单逻辑
- 注入线程池并准备线程任务
首先需要去注入execute执行器,专门用于实现秒杀订单,在这里我们先去获得一个单线程执行器SingleThreadExecuter,因为在当前业务中不需要特别快的处理速度。
其次需要去创建线程任务runnable。
而我们必须要在用户进行秒杀抢购之前就开启线程任务,并等待用户发起任务后第一时间获取到秒杀信息并去执行,也就是说要在当前类被初始化之后就立刻开始执行该秒杀任务。
所以在这里我们可以借助Spring提供的 @PostConstruct 注解来实现 当前类初始化完毕后立刻执行init方法,并且在execute执行器中去提交该秒杀任务,也就是可以直接执行run方法了。
在run方法中就可以去循环取出阻塞队列中的下单信息,并执行下单逻辑
4. 完善异步下单逻辑
实现runnable线程任务及run方法
改造createVoucherOrder创建订单方法
三、总结
使用JDK提供的阻塞队列存在的一些问题:这个阻塞队列使用的是JVM的内存,如果不对其加以限制,那么在高并发的情况下可能会有无数的订单对象会去创建并存入,那么可能会导致JVM内存溢出;即便我们为该阻塞队列设置了存储上限,但是当队列被存满,就无法再向其内部存储信息了,这就是内存限制问题;而且阻塞队列基于内存来存储的这些信息会因为服务宕机而全部丢失,这就是数据安全问题。