环境
PHP_VERSION=7.4
laravel/framework: ^7.0
复制代码
静态变量
- 很多编程语言对于静态变量的解释都是: 与程序有着相同生命周期的变量, 只初始化一次
- 不过由于
PHP的常用运行环境是php-fpm模式,每次请求结束进程就会被回收, 静态变量不会常驻内存(只会在此次请求生效) - PHP 官网是这么介绍的
变量范围的另一个重要特性是静态变量(static variable)。静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看下面的例子:www.php.net/manual/zh/l…
前言
- 项目中有以下伪代码逻辑: 因为数据库中的
json_data是一个json字符串,所以不必每次获取都解析, 使用static变量修饰符使得下一次访问不需要再次解析
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AttributeRequestLog extends Model
{
public function getJsonData($key)
{
static $jsonData;
if (is_null($jsonData)) {
$jsonData = json_decode($this->attributes['json_data'], true);
}
return $jsonData[$key] ?? null;
}
}
复制代码
因为之前没上队列处理异步任务, 程序一直没问题. 直到某一天上了队列之后, 有同事反馈, 有异常数据上报. 赶紧排查了一下日志, 发现队列中的日志打点数据有问题,随后增加更多打点, 最后定位到了这个地方.
- 由于
Laravel的队列采用CLI运行模式, 这时候处理的任务都是后台运行 - 队列启动时载入代码, 直到队列进程被杀死, 否则代码也不会更新,
分析源码
- 队列的启动命令:
php artisan queue:work - 找到启动文件
src\Illuminate\Queue\Console\WorkCommand.php是一个继承于Illuminate\Console\Command的类,运行artisan的时候, 会运行其的handle方法
![图片[1]-static 静态变量引起 Laravel 中队列一个 Bug-一一网](https://www.proyy.com/skycj/data/images/2021-07-06/f82e966dc8bb60ef59788b1ac3bb910c.jpg)

- 实际上是拿到队列的驱动,然后转到
worker去运行任务, 传递了一个参数once是否只运行一个任务,这里我们直接查看daemon方法 - 转到
src\Illuminate\Queue\Worker.php的daemon方法

- 前面三行代码去监听退出信号,然后主动退出进程
- 下一行的
$lastRestart是缓存中获取一个时间戳,用于之后的主动退出进程,这个时间戳只会被php artisan queue:restart重置 - 所以可以用
queue:restart这条命令去停止队列进程(并不会自动启动队列进程,可以配合Supervisor来自动重启) - 接下来是一个死循环,来达到进程不被杀死
- 第一个逻辑判断死看程序是否已经启动的维护模式,强制运行等等,就是队列任务是否能继续处理的前置判断
- 所以我们想临时暂停队列进程,可以向进程发送一个
SIGUSR2信号,这时候队列进程处理完当前任务下一次就会停止,当想继续处理的时候,再发送一个SIGCONT信号 - 然后到
getNextJob这个方法去配置的队列驱动(redis, database 等等)里获取下一个待处理的任务 - 如果支持异步扩展,
registerTimeoutHandler对任务的超时做了一些处理, 如果任务超时了, 那么就结束任务 - 下一步如果取出来的没任务, 那么就程序休眠, 否则就运行任务, 这里可以去看一下任务的实际运行代码
![图片[2]-static 静态变量引起 Laravel 中队列一个 Bug-一一网](https://www.proyy.com/skycj/data/images/2021-07-06/b7812f9132788e2ab70bd256a9117dec.jpg)

- 这里我们直接看
fire方法即可, 然后找到对应的队列驱动类,继承了父级的fire方法

- 实际上是反射了这个
job类然后调用它对应的方法 - 循环前的最后一个代码块就是
stopIfNecessary, 看进程是否需要终止, 前面说的queue:restart也是在这里处理
- 所以当我们使用静态变量的时候,虽然每次反射实例化了一个新的
job,但实际上job去拿模型的属性的时候,static变量是一直没有发生变化的,这就导致了前面说的Bug
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)