队列PHP_VERSION=7.4laravel/framework:^7.0静态变量很多编程语言对静态变量的解释是:与程序生命周期相同的变量,只初始化一次,但由于以PHP常用的运行环境是php-fpm模式。每次请求结束都会回收进程。静态变量不会留在内存中(只对本次请求有效)。PHP官网介绍的变量范围的另一个重要特性是静态变量。静态变量仅存在于局部函数范围内,但当程序执行离开该范围时,它们的值不会丢失。看下面这个例子:https://www.php.net/manual/zh/language.variables.scope.php前言项目中有如下伪代码逻辑:因为数据库中的json_data是一个jsonstring,不需要每次获取都解析,使用static变量修饰符,这样下次访问不需要再解析attributes['json_data'],true);}返回$jsonData[$key]??无效的;}}因为之前队列上没有异步处理任务和程序一直没问题。直到有一天,我加入队列的时候,有同事反映,上报数据异常。赶紧查看日志,发现是队列中的日志数据有问题。然后我添加了更多的日志记录数据,终于找到了这个地方。由于Laravel的队列采用CLI运行方式,此时处理的任务是在后台运行队列启动时加载代码,直到队列进程被kill掉,否则代码不会更新,分析源码的启动命令queue:phpartisanqueue:work找到启动文件src\Illuminate\Queue\Console\WorkCommand.php是继承自Illuminate\Console\Command的类。在运行artisan的时候,会运行它的handle方法来实际获取队列的driver,然后去worker去运行task,传一个参数一次就只运行一个task,这里我们直接查看daemon方法去src\Illuminate\Queue\Worker.php的daemon方法中前三行代码监听退出信号,然后主动退出进程下一行的$lastRestart是从缓存中获取的时候时间戳用于稍后主动退出进程。这个时间戳只有通过phpartisanqueue:restart才会重置,所以可以使用queue:restart命令停止队列进程(队列进程不会自动启动,可以配合Supervisor自动重启)后面跟着死循环,实现进程不被杀死首先逻辑判断看程序是否启动了维护模式,强制运行等,是队列任务是否可以继续处理的预判断所以我们如果你想暂时挂起队列中的进程,可以给进程发送一个SIGUSR2信号。此时队列进程在处理完当前任务后会在下一次停止。当你想继续处理时,发送一个SIGCONT信号,然后进入getNextJob方法配置队列驱动。获取下一个要处理的任务在(redis、数据库等)如果支持异步扩展,registerTimeoutHandler对任务超时做一些处理。如果任务超时,则结束任务。程序休眠,否则任务将运行。这里可以看一下任务的实际运行代码。这里我们可以直接看fire方法,然后找到对应的队列驱动类。从父类继承的fire方法实际上反映了作业类。然后调用它对应的方法,循环前的最后一个代码块是stopIfNecessary,看是否需要终止进程。上面提到的queue:restart也是在这里处理的,所以当我们使用静态变量的时候,虽然每次反射都会实例化一个新的作业,但是实际上作业去获取模型的属性的时候,静态变量并没有发生变化,导致上述Bug的原链接https://www.shiguopeng.cn/archives/516
