风险提示
使用Stone的一个很重要的事情是, 需要始终对内存使用抱有敬畏之心。 在未充分了解风险前, 请不要在实际项目中轻易尝试,否则可能产生非常严重的后果!!
比如,Laravle的Cookie实现了单例模式:
<?php
namespace Illuminate\Cookie;
use Illuminate\Support\ServiceProvider;
class CookieServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('cookie', function ($app) {
$config = $app['config']['session'];
return (new CookieJar)->setDefaultPathAndDomain($config['path'], $config['domain'], $config['secure']);
});
}
}
这样,开发者在任何地方可以往Response里追加cookie, 使用:
Cookie::queue('key', 'value', 60);
所有的Cookie都被维护在一个数组中,等待Response发送到客户端:
<?php
namespace Illuminate\Cookie;
use Illuminate\Support\Arr;
use Symfony\Component\HttpFoundation\Cookie;
use Illuminate\Contracts\Cookie\QueueingFactory as JarContract;
class CookieJar implements JarContract
{
/**
* The default path (if specified).
*
* @var string
*/
protected $path = '/';
/**
* The default domain (if specified).
*
* @var string
*/
protected $domain = null;
/**
* The default secure setting (defaults to false).
*
* @var bool
*/
protected $secure = false;
/**
* All of the cookies queued for sending.
*
* @var array
*/
protected $queued = [];
在原来的PHP-FPM下,这不会有任何问题,因为请求结束后PHP对释放掉所有资源。 但Stone常驻内存后, 请求的资源没有在请求结束后释放, 因为cookie对象被ioc容器引用,PHP GC不会回收这部分内存。因此下一个请求再使用Cookie的时候获取的是同一个Cookie对象。
比如, 第一个请求:
Cookie::queue('first', 'test', 60);
这个时候queue数组里已经有first的cookie。
第二个请求:
Cookie::queue('secend', 'test', 60);
由于获取的还是上一个cookie对象, 因此queue数组包含first和second的cookie!
想像下, 如果这是一个关于Session Id的cookie, 会产生多么严重的后果。
同样,Laravel的Auth对象, 也可能出现类似的问题。
内存泄露什么的可能只是影响稳定性, 但是这个处理不当会带来难以估计的损失, 这是使用Stone或者类似常驻内存方案的最大风险。
Stone通过实例快照来解决这个问题。 简单来说就是在对象建立的时候把对象的当前状态保存下来, 当请求结束后通过保存的数据把实例恢复成初始化的状态,这样就可以避免这个问题。
在config/stone.php的配置中,你可以定义哪些对象需要建立快照。
// 需要建立快照的绑定
'snap_bindings' => [
'view',
'cookie',
'session',
'session.store',
//'config', // debugbar 需要重置config
],
有关资源复用的更多细节, 请查看理解资源复用这个章节。
开始使用Stone
如果你已经了解这样的潜在风险, 并对此信心满满, 那开始使用吧: