背景
异步削峰,目的是削峰,方式是异步。面对瞬时压力,都需要异步削峰,其关键都在于拉长时间线,削平毛刺,最终整体提升吞吐量。
核心流程
提交下单任务:用户发出下单请求后,先要获取订单许可,若获取成功才能将下单任务提交给消息队列进行处理;
下单任务轮询:异步处理,不会立刻返回结果。所以用户需要一定频率来轮询处理结果;
实现原理
下单许可
所谓下单许可,指的是在秒杀品开始秒杀前,根据秒杀品的库存配置特定比例(可调节) 的下单许可,只有获得下单许可的用户才能提交下单任务。个例子,假如秒杀品A有库存10000个,那么我们可以将秒杀许可设定为10000*1.2
或10000**1.5
。
- 降低不必要的资源竞争与浪费。限制订单任务放入消息队列,如果不设置12000的下单许可,则可能有10,000甚至100,0000的用户将请求提交到任务队列,但是我们只有区区12000的库存,这会给队列和相关计算方造成巨大压力。
- 提升用户体验。有了下单许可之后,当许可被抢完的时候,我们即可立即向用户展示“售罄”或“暂无库存”等更为友好且贴近事实的提示,不存在忽悠,是真没有库存了。
- 确保所有库存可以卖出。按照1.2或1.5等比例设置高出库存数量的下单许可,就是为了预留一定的Buffer,允许一些无效提交但不会影响整体售卖。
提交下单任务
异步处理过程中,提交下单任务是第一步,注意事项:
- 提交下单任务之前,应通过基础且必要的账号、安全相关的校验;
- 下单要加锁,防止抖动、连续点击等导致用户层面出现重复提交问题;
- 所提交的下单任务,应该具有明确且唯一的编码以便于跟踪,即 placeOrderTaskId,当用户获得提交许可时,应向用户提供placeOrderTaskId用以后续的结果轮询;
- 同一用户、同一秒杀品,不应出现重复提交(可以通过placeOrderTaskId判断) ;
- 下单许可的设计应结合本地缓存+中心化缓存,以降低网络请求负载并提高处理效率;
- 在处理本地缓存和中心化缓存时,要着重注意过期时间的设置和更新时的锁竞争问题;
生成 placeOrderTaskId 代码
通过userId 和 itemId 生成 placeOrderTaskId 可以保证同一用户只能购买一个商品
/**
* 订单任务id
*
* @param userId
* @param itemId
* @return
*/
private String generatePlaceOrderTaskId(Long userId, Long itemId) {
String toEncrypt = userId + "_" + itemId;
return DigestUtils.md5DigestAsHex(toEncrypt.getBytes());
}
复制代码
生成下单许可代码
结合本地缓存 + 分布式缓存的实现
private final static Cache<Long, Integer> availableO