关于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 垃圾回收
同样,在回收一个对象时,需要销毁对象的所有属性,父类属性等等,以及调用析构函数等等
如果对象复用,这些操作将都不需要,我们只需要执行一次,即可复用
注:步骤等本人并没有详细了解,只根据本人经验进行模糊以及通俗解释
对象池
在上面的说明中,我们已经知道了对象复用的好处,那么如果我有2个请求同时进来呢?3个?10个?(多请求单进程处理需要php实现异步网络服务器,或者swoole协程网络服务器)
很明显,如果是多个请求同时处理,一个对象是不够用的.
那我们能不能先new 10个,或者100个,1000个,然后每次请求进来就分一个,标记为正在使用,其他请求不能再用这个,请求结束后标记为未使用,等待请求使用?
这个操作,就是对象池。
顾名思义,对象池是一个池子,每次我们需要对象时从里面拿一个,用完再放回去,这样又实现了对象复用,又实现了能同时处理多个请求
对象池的意义
上面我们可能发现了,对象池如果对象太少,比如只有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)