乐趣区

关于php:static-静态变量引起-Laravel-中队列一个-Bug

环境

PHP_VERSION=7.4
laravel/framework: ^7.0

动态变量

  • 很多编程语言对于动态变量的解释都是: 与程序有着雷同生命周期的变量, 只初始化一次
  • 不过因为 PHP 的罕用运行环境是 php-fpm 模式, 每次申请完结过程就会被回收, 动态变量不会常驻内存(只会在此次申请失效)
  • PHP 官网是这么介绍的

变量范畴的另一个重要个性是动态变量(static variable)。动态变量仅在部分函数域中存在,但当程序执行来到此作用域时,其值并不失落。看看上面的例子:https://www.php.net/manual/zh/language.variables.scope.php

前言

  • 我的项目中有以下伪代码逻辑: 因为数据库中的 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 办法


  • 实际上是拿到队列的驱动, 而后转到 worker 去运行工作, 传递了一个参数 once 是否只运行一个工作, 这里咱们间接查看 daemon 办法
  • 转到 src\Illuminate\Queue\Worker.phpdaemon办法
  • 后面三行代码去监听退出信号, 而后被动退出过程
  • 下一行的 $lastRestart 是缓存中获取一个工夫戳, 用于之后的被动退出过程, 这个工夫戳只会被 php artisan queue:restart 重置
  • 所以能够用 queue:restart 这条命令去进行队列过程 (并不会主动启动队列过程, 能够配合Supervisor 来主动重启)
  • 接下来是一个死循环, 来达到过程不被杀死
  • 第一个逻辑判断死看程序是否曾经启动的保护模式, 强制运行等等, 就是队列工作是否能持续解决的前置判断
  • 所以咱们想长期暂停队列过程, 能够向过程发送一个 SIGUSR2 信号, 这时候队列过程解决完当前任务下一次就会进行, 当想持续解决的时候, 再发送一个 SIGCONT 信号
  • 而后到 getNextJob 这个办法去配置的队列驱动 (redis, database 等等) 里获取下一个待处理的工作
  • 如果反对异步扩大,registerTimeoutHandler对工作的超时做了一些解决, 如果工作超时了, 那么就结束任务
  • 下一步如果取出来的没工作, 那么就程序休眠, 否则就运行工作, 这里能够去看一下工作的理论运行代码

  • 这里咱们间接看 fire 办法即可, 而后找到对应的队列驱动类, 继承了父级的 fire 办法
  • 实际上是反射了这个 job 类而后调用它对应的办法
  • 循环前的最初一个代码块就是 stopIfNecessary, 看过程是否须要终止, 后面说的queue:restart 也是在这里解决


  • 所以当咱们应用动态变量的时候, 尽管每次反射实例化了一个新的 job, 但实际上job 去拿模型的属性的时候,static变量是始终没有发生变化的, 这就导致了后面说的Bug

原文链接 https://www.shiguopeng.cn/archives/516

退出移动版