绕过条件竞争多次领取优惠卷,这种问题出现的原理是:多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。通常的web处理方式是通过单线程线性完成的,如果出现多线程并发请求的情况,数据处理逻辑就可能出现异常。
接口:/api/coupon/receive
修复建议:
一般是使用mysql的锁机制、或者使用redis的原子性、单线程特性来解决。
本次修复采用了限制接口请求频率的方案来解决,因为我们在遇到这个bug之前已经实现了限制接口频率的功能,另外急于工期,所以临时这样解决。最终还是计划使用redis来从根源解决这个问题。
该问题可延伸出秒杀场景下的库存超卖、签到场景下的多次签到领取奖励等。大家可以测试排查下相关场景是否存在该问题。
修复方法:
1:安装 composer安装 topthink/think-throttle 依赖;
composer require topthink/think-throttle
注意:执行composer 安装依赖命令的时候最好先git提交一下代码,或者备份一下, 因为依稀记得当时安装这个依赖的时候导致项目提交git的时候出现了一个关于git子模块的问题,当时排查出来是因为composer更新了一个名为alipaysdk/easysdk的依赖,这个依赖又依赖于xin/container这个,然而这个xin/container依赖文件夹内部有.git的文件夹,因为我对git子模块不太熟悉,所以当时git提交报错后,我直接将xin/container文件夹内的两个子目录里的.git删掉即可。
2:对领取优惠券接口使用频率限制配置(也可以对任意想要限制频率的接口进行设置)
说明:
1. 对相关接口配置\think\middleware\Throttle中间件,1/10表示10秒内仅允许一个请求;
2. key中一定要使用到用户的唯一uid;
3. 另外就是这个中间件一定要放在认证中间件AuthTokenMiddleware后面,否则request中获取不到用户的uid信息。