专栏首页北溟有鱼QAQ关于php对象池

关于php对象池

生命周期

对象池需要从php的生命周期说起,php的应用大部分都是web网站,而大部分web网站使用的都是cgi模式进行运行的,导致php生命周期跟随着请求结束而结束,从而没有对象池的概念

cgi模式的一次请求可以分为以下几步:

  1. 用户请求
  2. web服务器(apache,nginx,iis等)接收请求
  3. 服务器通过cgi协议调用php,运行php文件
  4. php文件处理逻辑,返回数据,php进程 销毁/回收(该次执行的php变量内存等全部回收)
  5. web服务器接收数据,返回给用户,web服务器关闭连接
  6. 用户接收数据,用户关闭连接

在这个过程中,是根本没有对象池概念的,因为php的变量是随着用户的请求而销毁,无法把php的变量留给下一个用户进行执行 这就导致了如果用户1请求,需要new 一个对象,那么用户1请求完毕将会销毁,用户2需要重新再new一个对象,再销毁。。。。。如此反复。

那么,php能实现一个请求进来,结束之后保存对象,然后第二个请求进来的时候,初始化下对象属性(不初始化属性会造成第二个请求用到第一个的垃圾数据),然后让第二个请求直接使用第一个请求new好的对象吗?

php-cli模式

php-cli命令行模式,它和传统cgi不同,cgi是跟web服务器等交互,而web服务器一般是跟使用浏览器的用户交互的 而php-cli是命令行模式,是直接跟开发者交互,由开发者编写程序,然后直接输入 php test.php

进行运行php脚本

为什么要讲php-cli模式呢?

在php-cli模式中,开发者可以编写不中断运行的代码,以及可以自行维护运行php的进程,可以实现一个web服务器和用户交互。

类似于这样:

<?php
//以下为伪代码
class User{
    protected $user;
    public function __construct()
    {
    }
 
    /**
     * @return mixed
     */
    public function getUser()
    {
        return $this->user;
    }
 
    /**
     * @param mixed $user
     */
    public function setUser($user): void
    {
        $this->user = $user;
    }
}
 
$phpServerStart=true;//自己实现一个php的web服务器
$user = new User();
while(1){
    //第一次循环,获取到了一个用户请求的信息
    $user = [
        'name'=>'tioncico'
    ];//获取到第一个用户请求我们自己实现的web服务器的数据
    $user->setUser($user);
    echo json_encode($user->getUser());//输出数据到第一个用户,理论上php-cli是跟开发者交互的,echo没法直接输出给用户,该知识点下面将补充
     
    //第二次循环,没有用户请求,继续循环下去
     
//    第三次循环,用户请求
    $user = [
        'name'=>'显示可'
    ];//获取到第一个用户请求我们自己实现的web服务器的数据
    $user->setUser($user);
    echo json_encode($user->getUser());//输出数据到第一个用户,理论上php-cli是跟开发者交互的,echo没法直接输出给用户,该知识点下面将补充
     
    //无限循环下去,不断的获取用户的请求
}

在这份代码中,可以看出:

  1. 我们在程序一开始,自己实现了一个web服务器
  2. 先new 了user对象
  3. while 1死循环,只要获取到了用户请求,则处理数据
  4. 获取到了用户1数据,直接填入new好的对象中,并echo回去
  5. 再次获取到了用户2数据,覆盖之前用户1的对象属性,并echo回去

在这份代码中,为什么$user对象可以复用呢?原因就在于我们使用php-cli模式,用php自己实现了web服务器的部分功能,让php接管了web服务器,这样使得用户请求的生命周期,限制在了while(1)里面,而用户请求结束之后,并不会销毁while(1)外面的变量

对象复用

对象复用以及不复用的效率

那么这个时候可能会有人问?new一个对象,多大事啊!给它new不就得了!针对这个问题,我们可以来测试下new一个对象的消耗有多大

新建一个测试脚本:

<?php
 
class Test
{
    protected $a;
 
    public function __construct($a)
    {
        $this->a = $a;
    }
 
    public function setA($a)
    {
        $this->a = $a;
    }
 
    public function getA()
    {
        return $this->a;
    }
}
$startTime = microtimeFloat();
for ($i = 0; $i < 10000; $i++) {
    $test = new Test($i);
    $test->getA();
}
echo "对象不复用耗时" . (microtimeFloat() - $startTime) . '秒' . PHP_EOL;
 
$startTime = microtimeFloat();
$test = new Test(0);
for ($i = 0; $i < 10000; $i++) {
    $test->setA($i);
    $test->getA();
}
echo "对象复用耗时" . (microtimeFloat() - $startTime) . '秒' . PHP_EOL;
 
 
function microtimeFloat()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

测试结果:

/usr/local/php-7.2.2/bin/php /home/tioncico/PhpstormProjects/test/test.php
对象不复用0.0012578964233398秒
对象复用 0.00068378448486328秒

可看出,在对象复用情况下,效率比不复用情况快了一倍,可能有人会说:缺这么性能算什么?根本看不出啊!

这是因为测试文件的类是最简单的类,如果是复杂点的,例如继承,多重继承构造函数,析构函数,以及triat等等复杂对象,花费的cpu可就不止这些了

为什么复用对象会比不复用快?

这个需要从2方面进行讲解

php实例化对象步骤: 如果讲php实例化的底层的话,大家可能听不懂,我也不懂底层,所以本人用通俗的方法讲解下php实例化对象需要做的事情(步骤前后顺序可能有错)

  1. 实例化对象,检查对象属性,方法,结构等
  2. 属性初始化
  3. 对象父类属性初始化
  4. 构造函数初始化

可以看出,new 一个对象,所做的事跟对象的复杂度有关,比如类继承,类接口实现,检查对象继承接口等是否有错,初始化属性,调用构造函数等等

php 垃圾回收

同样,在回收一个对象时,需要销毁对象的所有属性,父类属性等等,以及调用析构函数等等 如果对象复用,这些操作将都不需要,我们只需要执行一次,即可复用

注:步骤等本人并没有详细了解,只根据本人经验进行模糊以及通俗解释

对象池的意义

上面我们可能发现了,对象池如果对象太少,比如只有10个,那10个都被人用了,岂不是第11个人没得用了?

答案是对的

那为什么不直接设置10000个,想多少人用就多少人用?

理论上是这样的,但是对象池的意义,就是限制并发的大小,防止服务器负载太高而进行宕机。

例如:

假设没有对象池,也没有对象复用,在传统web模式下,假设进程也有100,10000个,一个请求进来需要消耗1%的cpu
当100个请求进来的时候,cpu已经为100%,勉强全部能运行
而出现101个请求之后,某个请求会因为cpu资源不够,处理将会变慢,直到其他请求处理好一个,腾出1%去处理新的请求

如果当出现200个请求,cpu由于分时调度(尽量使得所有请求处理的时间尽量平均),会使得所有请求平均响应时间慢一倍(如果有一个进程正常响应,那么就说明有几个请求需要慢2倍甚至更多)

再到后面,将会出现只能响应少数请求,其他请求全部超时无法正常响应的宕机情况

上面的cpu资源争夺是其一,其二是消耗内存,如果同时处理太多进程,还有可能造成服务器内存不够。

对象池的意义就在于此:

设定合理的对象池数量,当超出对象池数量时,让请求等待或者直接提示系统繁忙,保证其他请求进行正常响应,保证服务器的运行正常

例如设置了100个对象 第101个请求进来时,使其等待3秒,3秒内如果有对象回收,则直接给101个请求使用,否则3秒后告诉该请求服务器繁忙,请稍后再试,避免出现服务器调度混乱,导致宕机

php什么时候会用到对象池 由于对象池的特性,它只出现在单进程处理多个请求情况而出现(例如java的多线程同时处理),而php中大部分情况是没有的,目前只有在swoole协程中使用较多,或者在php异步网络服务器中使用

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PHP自动加载与composer自动加载

    当我们编写面向对象的程序时,通常是将类分别放在不同的文件中。但这样一来,当我们调用其他类的时候,则需要先手动引入该文件(否则会因为当前程序中没有该类名的类而报错...

    北溟有鱼QAQ
  • PHP对接硬件当中用到的函数

    北溟有鱼QAQ
  • 如何使用Git(一)

    此时本地仓库的提交以及版本控制已经实现 建议大家在学东西的时候一定不要光看,要实际敲出来,毕竟不实际操作,你就不会发现操作中出现的问题,实践是检验真理的唯一标准

    北溟有鱼QAQ
  • MongoDB Web界面 管理工具Rockmongo的安装

    RockMongo是PHP5写的一个MongoDB管理工具。

    似水的流年
  • MongoDB Web界面 管理工具Rockmongo的安装

    RockMongo是PHP5写的一个MongoDB管理工具。 通过 Rockmongo 你可以管理 MongoDB服务,数据库,集合,文档,索引等等。...

    似水的流年
  • linux学习第四十二篇:限定某个目录禁止解析php, 限制user_agent,PHP相关配置

    限定某个目录禁止解析php 虚拟主机配置文件添加的核心配置内容: <Directory /data/wwwroot/111.com/upload> ...

    用户1215343
  • 历史上争议最大的编程语言是谁?

    php估计目前是存在争议最大的语言,争议声一直不绝于耳。这是一门优势巨大,缺陷也一样巨大的编程语言。一直在争议中进度,企业招聘的岗位也是越来越多,这么多人参与进...

    程序员互动联盟
  • PHP-fpm 远程代码执行漏洞(CVE-2019-11043)分析

    国外安全研究员 Andrew Danau在解决一道 CTF 题目时发现,向目标服务器 URL 发送 %0a 符号时,服务返回异常,疑似存在漏洞。

    Seebug漏洞平台
  • 初学Swoole:PHP7安装Swoole的步骤

    本篇文章给大家带来的内容是关于初学Swoole:PHP7安装Swoole的步骤,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

    叫我可儿呀
  • Swoole 初体验之安装篇

    hedeqiang

扫码关注云+社区

领取腾讯云代金券