Heim  >  Artikel  >  Backend-Entwicklung  >  Web提速:避免php session拖慢运行速度

Web提速:避免php session拖慢运行速度

WBOY
WBOYOriginal
2016-06-23 13:58:311283Durchsuche

Web提速:避免php session拖慢运行速度

 

一、WHAT--并发访问,阻塞执行
1.1 不使用session

文件index.php:

<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script><script type="text/javascript">$(document).ready(function(){$.ajax({url:"/ajax.php"});$.ajax({url:"/ajax2.php"});$.ajax({url:"/ajax3.php"});});</script>


文件ajax.php、ajax2.php、ajax3.php的内容都是

<?phpsleep (1);echo "php script run";


  每个请求都sleep 1s,jq的ajax请求是异步的,也就是说这三个请求基本上是同时发出的,理论上最好的情况是浏览器等待1s,3个接口全部返回。

访问http://localhost,在chrome下查看测试结果:

图 1.1 不使用session测试结果

  测试结果基本上跟理论一致

1.2 使用session

现在我们把文件ajax.php、ajax2.php、ajax3.php都改为

<?phpsession_start ();sleep(1);echo "php script run";



这样做会什么有什么区别吗?我们直接看测试结果:

图 1.2 使用session测试结果

不是每个请求只执行1s钟吗?为什么ajax2.php消耗了2s,ajax3.php消耗3s?

二、WHO -- Session锁

session锁的官方定义:

        Session data is usually stored after your script terminated without the need to call session_write_close(), but as session data is locked to prevent concurrent writes only one script may operate on a session at any time.

  大致的意思是: 从调用session_start()开始,直到显示调用session_write_close()或脚本结束,session的数据都会被锁起来,届时同一个SESSIONID用户的请求会被阻塞。

从图1.2的测试结果看到,ajax2.php与ajax3.php分别多执行了1s和2s,很明显session_start()操作造成了阻塞。

  当index.php同时访问ajax.php、ajax2.php、ajax3.php,ajax.php首先执行,此时ajax2.php与ajax3.php都在等在ajax.php释放session锁,都消耗1s。

  当ajax.php执行完成,释放了session锁,ajax2.php与ajax3.php再次竞争session锁,同理ajax3.php又等待了1s钟。所以我们得到的结果:

  ajax.php 消耗1s

  ajax2.php 消耗2s

  ajax3.php 消耗3s

三、WHEN -- 什么时候会触发Session锁

  在章节二中,session锁定义为session_start()开始即会触发session锁,所以对于现有大部分php框架(使用原生php session的情况下)存在session锁造成的用户请求阻塞的问题。试想,有2个请求,其中请求A需要3s钟才能返回结果,另外请求B仅需要10ms即能返回,前端同时请求这两个接口,假如后台先处理了A请求,那么B请求就要等待3s后再执行10ms才能返回结果。但是最优的情况是,同时发起请求,10ms后收到B请求返回,3s后收到A请求返回。

四、WHY -- Session内部执行机制

  默认情况下php session采用文件为服务端存储介质。在php session模块的源码中,有一个比较重要的结构体:


图4.1 php session模块结构体 ps_module_struct

  该结构体里面的几个函数指针分别对应了session操作的open、close、read、write、destory、gc回收等功能。看到这6个函数,是否想起了php的SessionHandlerInterface接口(版本>=php5.4)?

图4.2 php SessionHandlerInterface接口

  用这个接口配合session_set_save_handler可以重写session的这几个关键操作。于是我们可以使用以下代码来探探php session在一个请求的生命周期中的运行顺序:

图4.3 session运行顺序测试代码

测试结果:

  由测试结果可知一般的session执行顺序,在session_start()调用时,php回去调用open和read操作,脚本执行结束后(输出了php script run),再会调用write和close操作。

现在我们不妨做一个大胆的猜想,php 在 session的open或者read操作时,开启了session锁,并write或close后释放session锁。这样的猜想也符合我们在章节1的测试结果。

  为了验证我们的猜想,就需要去php的源码探个究竟了。在php源码文件/ ext / session / mod_files.c中可以看到默认session的6个重要操作的部分实现。在156行(点击打开链接),看到open操作有一个打开文件操作:flock(data->fd, LOCK_EX);该操作以互斥锁定的方式打开文件。在110行关掉文件close(data->fd);。

  看到这里,我们应该得到的结论是:

在默认情况下,所谓的php session锁其实就是文件锁

  所以,当我们使用session_set_save_handler来自定session操作,改用memcache或其他介质时,只要我们在SessionHandlerInterface的接口中没有锁的逻辑,那么session锁自然也不会存在。作者私下也做了这样的实验,实践证明也的确如此。

五、HOW -- 如何避免Session锁带来的阻塞现象

  首先,session锁不一定是坏事情,在一种情况下就非常好用,例如某接口对与同一个用户的请求默认同一时刻只能执行一次。这种时候,就可以用seesion_start()和session_write_close()把要阻塞的代码括起来。非常简单暴力实用。

  但是大部分时候我们还是要避免这种锁的存在,解决方案:

1、在用完session的时候就马上session_write_close()掉,释放session锁

2、采用没有锁的session操作,如章节4中所说的用session_set_save_handler来自定义一个没有锁的session操作。

3、再使用默认php session时,个人比较中意的一个方案:大部分情况下,我们对session的操作基本上都是读操作,写操作一般都比较少。这种时候,我们可以自己写一个session类。

构造函数:将session读入cache,关闭session锁

写操作:打开session锁,写入值,关闭session锁

读操作:直接读cache

部分代码如下:

//将session读入全局变量$_SEESIONstatic private function init(){if(self::$not_init){ session_start(); session_write_close(); self::$not_init = false;}}


//读sessionstatic public function get($name){self::init();return $_SESSION[$name];}


//写sessionstatic public function set($name, $val){session_start();$_SESSION[$name] = $val;session_write_close();}


注意:如果是写操作频繁的操作,就不适合使用该方法。

 

 


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn