5.6 对象池
最后更新于:2022-03-31 23:47:49
# 5.6 对象池
对象池服务可以减少从头创建每个对象的系统开销。在需要对象时从池中提取,在使用完对象时,把它放回池中,等待下一个请求。对象池使你能够控制所使用的对象数量。在PHP的长驻进程模式下,对象池尤其重要,由于PHP的GC缺陷,在高并发下,PHP常驻进程内直接通过new创建对象,会导致PHP进程占用大量的内存,而且很容易出现OOM(out of memory)。
## 主要特性
- 创建固定数量的对象;
- 需要时从池中提取,不需要时归还池中;
- 自动归还对象;
- 根据有效期和使用次数淘汰对象
## 获取对象池
```
$this->getContext()->getObjectPool()
```
理论上基于MSF框架的任何代码都可以通过请求上下文对象来获取对象池对象
## 获取对象
如获取Http Client对象:
```
$client = $this->getContext()->getObjectPool()->get(\PG\MSF\Client\Http\Client::class)
```
### 初始化对象
虽然我们使用了对象池模式,但我们也可以使用__construct()方法,无须额外调用初始化方法。
```
$this->getContext()->getObjectPool()->get(FeedComment::class, ['构造参数1', '构造参数2', ...]);
```
通常情况下,框架内置的类均`use MI;`,则可以直接使用`$this->getObject($className, ['构造参数列表'])`来创建对象,第三方lib也可使用trait来扩展类的功能。
### 资源释放
className::destroy()
每一个类,理论上都需要定义destroy()方法,用于资源的手工释放,但是不需要显示调用,在请求结束时由框架自动调用。
对于和请求相关的数据,理论上我们是需要在请求结束后释放相关资源,框架对于资源释放的策略及优先级如下:
1. 响应请求后调用Controller::destroy()
2. 依次调用当前请求所使用的对象的destroy()方法
3. 将所有public的类属性的值设置为初始值
通常情况下,destroy方法用于处理private,protected的类属性,public由框架自动清理。
如下示例:
```php
<?php
/**
* Demo模型
*/
namespace App\Models;
use PG\MSF\Models\Model;
class Demo extends Model
{
public $pro1;
public $pro2;
protected $pro3;
private $pro4;
public function __construct($pro1, $pro2)
{
parent::__construct();
$this->pro1 = $pro1;
$this->pro2 = $pro2;
$this->pro3 = "protected";
$this->pro2 = "private";
}
public function getMockIds()
{
// 读取配置后返回
return $this->getConfig()->get('params.mock_ids', []);
}
public function destroy()
{
parent::destroy();
$this->pro3 = null;
}
}
```
1. pro1,pro2属性由框架自动在请求结束后赋值为初始值
2. pro3由业务手工在destroy()内清理
3. pro4长驻进程,永久使用
### 资源释放级别
除了上述资源释放策略,php-msf还提供自定义的资源释放策略,只需要在获取对象时提供第三个参数,如:
```php
function DS()
{
$this->getObject(Demo::class, [1, 2], \Marco::DS_PUBLIC | \Marco::DS_PROTECTED);
}
```
1. Marco::DS_PUBLIC 为默认策略
2. Marco::DS_PROTECTED 为释放protected属性
3. Marco::DS_PRIVATE 为释放private属性
4. Marco::DS_NONE 不释放任何属性
## 对象池实现原理
![对象池实现原理](../images/对象池实现原理.png "对象池实现原理")
## PHP进程内存优化
对象池模式对PHP进程占用的内存优化可以说“完全超出预期”,如下图所示:
![内存优化-30d.png](../images/内存优化-30d.png "内存优化-30d.png")
上图为近30天基于MSF重构的某业务其中一台机器的一个worker进程内存占用的监控数据,从图中有几点:
1. 5-10~5-27,近15天内存占用波峰达1.25G,波谷的值在持续的攀升;
2. 5-27~5-31,近5天内存占用从一个较低的点持续攀升;
3. 6-01之后,内存占用持续稳定在25M上下小辐波动;
在1阶段,框架大量类的对象是直接使用new关键字创建,完全依赖的PHP GC进行内存资源的回收;
在2阶段,框架采用对象池的方案完全重构大量的逻辑,效果很明显,但仍然有部分内存泄露;
在3阶段,优化了业务逻辑,完全按照“资源释放”的策略调整业务代码,内存占用已稳定。