本章节主要讲优惠券链路的黄金流程,喜闻乐见的领券。
领券是营销过程中用户参与度非常高的一种行为,领用的券可以作为现金或者打折的方式直接抵扣支付现金,实打实的能够得到优惠,在促活和拉新场景下作为常见的手段来使用。在成本cover住的情况下,能够实现平台和用户的双赢。
设计领券的流程最先考虑的一定是限制流量,领券是直接能面对消费者的,也就是,任何一个人如果有基本的技术,都可以通过反编译或者解析调用链接来进行刷券请求,也就是我们常说的刷子流量,这部分流量是一定要阻拦住的,不然会给集群造成很大的压力,会影响正常用户下单
其实针对于优惠券的调用方很多,但是也比较好分类,APP、PC、H5、小程序 ,我们针对每个业务调用方分配给独立的token,调用时发现token异常直接拒绝。
对于一些黑产,会通过一些手段获取很多账号或者一些小号,这样的话,我们需要借助外部系统的能力,来识别到这些用户是否有风险,不能给他发放优惠券。
除了券包的形式发券的场景,用户点击领券的时间是有极限的,也就是说一个用户通过手点的方式,1s钟能领取的券一定是有限的。如果超过了这个范围,我们可以认为是异常流量,直接拒绝请求。
针对发放的比较超值的券,如满100-99这种券,优惠力度很大,会导致用户请求量非常大,有可能就是人均点一下,这单个活动流量的qps就轻松上万了,为了保护系统,针对不同的活动我们可以设置不同的限流阈值,来保护系统,快速返回。
有些时候因为业务人员操作有误,出现券面额很高,但是无门槛,或者门槛很低,会造成很大的经济损失,这种批次数据通过动态黑名单的方式进行控制,可以及时止损。
前后端进行请求的时候,不能直接明文传输,使用加密的方式进行处理,到我们券系统进行解密,减少无效或者异常调用,同时也很大程度避免撞库的问题
到目前为止,此校验才会真正的对正常请求的用户有限制作用,基于坐标或者ip来确定用户能不能领取这张限制地域的优惠券。
如果批次的领取场景限制在了小程序,那么就算用户根据链接从pc浏览器打开或者在App打开,都是不能领取的,也可以作为大型公司推广新业务的引流来使用。
有些批次我们设置了一些领取限制,比如一天只能领一次,或者活动期间内只能领取一次等。避免出现一个人领了太多的优惠券,导致正常用户无法领取到的问题
提前先从缓存中查询该批次还有没有剩余库存,如果没有的话不需要进行后续操作了,也就是需要我们在优惠券批次领取完成以后,对该活动打上标记,可以提前结束请求。
针对两次一样的调用(请求Id一致),我们的返回结果应该一致,而不是发两次券。
锁的粒度为用户Id+活动Id+批次Id 的维度,防止出现一个用户点击多下同一批次券的时候出现数据不一致问题,不能简单的把粒度放到用户维度,因为我们实际的库存是放在了活动+批次维度下的,用户领取不同活动的券不会存在并发扣减库存的问题。出现被分布式锁限制住的情况,可以立刻返回文案,“活动火爆,请稍后再试” 等等。
使用事务主要为了解决的是库存扣减与用户领取记录的一致性,不能够出现库存扣减成功,但是记录失败的问题。
对于分时段的库存,要同时扣减时段库存和批次总库存,也需要保证一致性
首先数据存储的话我们使用jvm cache + redis + mysql ,首先我们的主流程是尽量少的使用mysql等关系型数据库,会导致我们的系统吞吐量很难提升到上w的水平。
redis是我们系统的核心中间件,扛住系统的主要流量,对于系统十分关键
首先,我们使用redis的部署要尽量使用cluster方式,保证集群的稳定性,方便后期的横向提升系统的配置方案
对于redis 的调用,保持最少调用原则,尽量避免出现一次前端调用 后边redis ops 几十倍的调用,这样等到流量来了,redis也很难扛得住。
使用多套集群,提升系统的吞吐量,很多没有必然关系的数据可以保存在多套集群里边,比如防重记录等和我们的批次库存没有关系,可以放到不同的集群实例里边去,针对不同的批次也可以在不同的集群里边记录。毕竟后边我们也会把数据持久化到mysql,不会出现不好统计的问题。目前我们库存相关的集群32套,保证日常调用的性能不会出现很大的瓶颈。
使用mysql主要是为了数据持久化和数据的分析,也可以做问题的分析用,实际的mysql写入可以在写完redis后异步写库,同步写的话问题也不大。
任何一个系统都无法保证CAP,对于我们业务系统来说,我们一般可以保证999或者9999,那百分之一或者千分之一,我们也要做好补偿措施