think-queue 静态变量污染问题探究

1.work: 单进程的处理模式;

无 daemon 参数 work进程在处理完下一个消息后直接结束当前进程。当不存在新消息时,会sleep一段时间然后退出;

因为执行一次就退出了,不存在污染问题。

有 daemon 参数 work进程会循环地处理队列中的消息,直到内存超出参数配置才结束进程。当不存在新消息时,会在每次循环中sleep一段时间;

这里会造成内存污染,

代码演示效果

①.先定义一个TestS测试类

<?php
namespace app\api\job;
class TestS
{
    public static $num = 0;
    public function __construct(){
        echo "TestS construct \n";
    }
    public static function testNum(){
        self::$num++;
        return self::$num;
    }
    public function __destruct() {
        //在 PHP 中有一种垃圾回收机制,
        //当对象不能被访问时就会自动启动垃圾回收机制,
        //收回对象占用的内存空间。而析构函数正是在垃圾回收机制回收对象之前调用的
        echo "TestS destructed \n";
    }
}

②.再定义一个push队列和消费队列的Job类

class TestJob  extends BaseJob
{
    public function sendJob(){
        $job='app\api\job\TestJob@doJob';
        $data=[
            'rand'=>rand(100000,999999),
            'time'=>date("Y-m-d H:i:s"),
        ];
        for ($i=0;$i<5;$i++){
            $res=Queue::push($job,$data,'test');
            if($res){
                echo ' push success ';
            }else{
                echo ' push fail ';
            }
        }
    }
    public function doJob(Job $job,$data){
        echo "------------------------\n";
        $testS = new TestS();
        echo "testS object address:".spl_object_hash($testS)."\n";; // 输出的内存地址 每次都不一样,说明每次调用都会重新new一个新的,之前的也会被系统回收掉
        TestS::testNum();
        echo "num:".TestS::$num.",address:".memory_get_usage(TestS::$num).",processId:".getmypid()."\n";
        $job->delete();
        (new \app\common\model\QueueLog())->log($job->getQueue(),$job->getName(),$data);
    }
}

③.执行队列代码

//先调用sendJob方法生产队列
(new \app\api\job\TestJob())->sendJob();
//windows cmd 命令执行队列进行消费,注意需要进入到thinkphp的根目录下执行,具体参数根据自己情况配置
D:\phpstudy_pro\Extensions\php\php5.6.9nts/php think queue:work  --sleep=3 --tries=10 --daemon --queue test

④.执行结果如下

------------------------
TestS construct  //$testS = new TestS(); new 一个对象很正常的调用了构造方法
testS object address:000000001b40ea86000000007ab35548  //打印出$testS 的内存地址
num:1,address:6815744,processId:23148 //静态变量 num进行了累加,但是内存地址不一样,应该是重新分配了内存地址,进程id都一样说明是同一个进程执行的
TestS destructed  //虽然$testS 的内存地址都一样但是确实是执行了销毁函数
Processed: app\api\job\TestJob@doJob
------------------------
TestS construct
testS object address:000000001b40ea86000000007ab35548 
num:2,address:8126464,processId:23148
TestS destructed
Processed: app\api\job\TestJob@doJob
------------------------
TestS construct
testS object address:000000001b40ea86000000007ab35548
num:3,address:8126464,processId:23148
TestS destructed
Processed: app\api\job\TestJob@doJob

从上面的三次执行结果可以看出,每次消费完一个任务后,通过new实例化的对象都会销毁,因为你每次都new一个新的,之前的对象就没有变量再进行引用,所以会自动销毁

num的静态变量值会每次进行增加,因为每次调用都进行了num++操作,内存地址有时候会变有时候没变,这个应该是php的内部逻辑问题,不再探究,有兴趣的可以在下面留言

2.listen: 父进程 + 子进程 的处理模式;

会在所在的父进程会创建一个单次执行模式的work子进程,并通过该work子进程来处理队列中的下一个消息,当这个work子进程退出之后;

所在的父进程会监听到该子进程的退出信号,并重新创建一个新的单次执行的work子进程;

通过上面的解释可以理解,每次都新开一个子进程,那么上一个子进程的所有内存空间都会被释放

代码不变执行消费命令代码如下:

D:\phpstudy_pro\Extensions\php\php5.6.9nts/php think queue:listen  --sleep=3 --tries=10 --queue test

结果显示如下:

TestS construct
testS object address:00000000558bd66800000000539bf6a0 //new 对象的内存地址不一样
num:1,address:6815744,processId:9240 //num变量的值都是1,没有进行累加增加,但是内存地址一样,只是重新分配了值而已,这个不影响,子进程id不一样
TestS destructed
Processed: app\api\job\TestJob@doJob
------------------------
TestS construct
testS object address:0000000030a78add000000004d467751
num:1,address:6815744,processId:20840
TestS destructed
Processed: app\api\job\TestJob@doJob
------------------------
TestS construct
testS object address:000000004c847d9d000000000c00640c
num:1,address:6815744,processId:7904
TestS destructed
Processed: app\api\job\TestJob@doJob

所以listen模式,除了每次新开和关闭子进程比较浪费资源之外,其他还是比较安全的,如果并发不是很高的情况下还是建议linsten模式,至少不会出现内存泄漏,变量污染的问题。

以上只是个人理解,如果不对的地方欢迎大家指正,或者你有更好的见解也可以留言

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oraclechaozi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值