背景?
项目中有一个秒杀业务使用?Laravel 的队列进行对未付款超时的订单,进行库存回收处理
问题
????????未付款超时的订单库存没有及时回滚,造成了少卖的和用户不能买的情况(因为我们针对商品有限购处理,限购也没有及时重置)
?
环境
[root@vdevops XXXXXXXXApi]# php artisan --version
Laravel Framework 6.20.27
[root@vdevops XXXXXXXXApi]# php -v
PHP 7.4.19 (cli) (built: May 4 2021 11:06:37) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
[root@vdevops XXXXXXXXApi]#
代码?
<?php
namespace App\Traits;
use App\Events\seckill\UserListCancle;
use App\Exceptions\InvalidRequestException;
use App\Models\SeckillGoods;
use Illuminate\Support\Facades\Redis;
trait Seckill
{
private static $taskGoods;
private static $todayTaskId;
private static $ingNodeId;
private static $nodeInfo;
private static $node;
private static $expiredBuy;
private static $info;
?????????从上面部分代码可以看到定义的是全局的静态成员变量,目的是为了避免在一段代码里重调用一个方法?,把结果缓存起来,提升代码运行效率
当排查问题时,一直以为是队列的进程服务挂掉啦,当重新启动队列服务后,库存回收服务就恢复正常,因为我们的秒杀业务是按天进行,所以等到第二天进行观测下,第二天又出现服务不能正常回收库存。
由此判定不是这里的问题,通过代码追查到还原库存的 recoveSeckillGoodsStock function 获取秒杀业务当天的 $todayTaskId 使用的是静态成员变量调用的,Laravel 的队列是常驻内存,采用CLI运行模式在后台运行,直到进程被杀死,否则代码不会更新。$todayTaskId的值一直启动队列那天时间的秒杀任务ID ,始终没有发生变化,所以导致还原库存服务一直不成功的bug。
?
静态变量
????????如果一个函数内定义的变量前使用关键字 static 来声明,那么该变量就是静态变量。一般函数内的变量在函数调用结束后,其存储的数据将被清除,所占的内存空间也被释放。而使用静态变量时,该变量会在函数第一次被调用时被初始化,初始化后该变量也不会被清除,当再次调用该函数时,这个静态变量不再被初始化,而能保存上次函数执行完后的值。可以说静态变量在所有对该函数的调用之间共享。
- 如果在函数中?unset()?一个静态变量,那么在函数内部此静态变量将被销毁。但是,当再次调用此函数时,此静态变量将被复原为上次被销毁之前的值。
- 直接给静态变量赋值null 就可以
变量范围的另一个重要特性是静态变量(static variable)。静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看官网下面的例子:https://www.php.net/manual/zh/language.variables.scope.php
?
总结
????????使用静态成员变量需注意使用场景,也忽略 Laravel 的队列是常驻内存,如果是正常用户在前端访问秒杀页面,使用静态成员变量当缓存是可以的,因为PHP的常用运行环境是php-fpm模式,每次请求结束进程就会被回收, 静态变量不会常驻内存(只会在此次请求生效) 。
还一点就是代码没有跟着业务和场景的不同进行代码隔离,还原库存的function 和前端页面访问的方法都在一个特性Traits里,?开发功能时就会造成疏忽,导致一些问题的出现。
|