session工作原理、存储目录、垃圾回收、存储优化等研究

一、Session工作原理

    http协议是WEB服务器与客户端(浏览器)相互通信的协议,它是一种无状态协议,服务器响应完之后就失去了与浏览器的联系。所谓无状态,指的是不会维护http请求数据,http请求是独立的,不持久的。而越来越复杂的WEB应用,需要保存一些用户状态信息。这时候,Session这种方案应需而生。PHP从4.1开始支持Session管理。

    session的工作原理:用户在客户端登陆网站时,被访问的PHP页面使用session_start()函数启动session,产生客户端唯一标识session_id(使用函数session_id()设置),当请求下一个页面时,PHP程序通过两种方式来获取客户端的session_id:一种是将session_id自动加入到GET或POST的URL中,默认情况下,变量名为PHPSESSD(在php.ini中的设置session name=PHPSESSD);另一种方式是通过cookie,将session_id 保存在cookie中,如下图所示的红色框,客户端的PHPSESSID的值与服务端的sess_后缀的值是相等的。由此可以看出,服务端产生客户端唯一标志的sessionid。

 

客户端:

服务端:



      保存在服务器端的session数据,不是保存在服务器端的内存中,而是保存在文件或数据库中。默认情况下配置服务器端的php设置(php.ini),通过文件的方式保存(session save_hander=files),使用读写文件的方式保存session数据,session文件保存的目录由session save_path指定,如:文件名为 sess_c72665af28a8b14c0fc61afeb59b51b,文件中的数据即是序列化之后的session数据,如果访问量大,还可能产生多个session文件,这时可以设置分级目录保存session文件

session save_path="N;/save_path"(N为分级的级数,save_path为开始目录)。对session数据写入时,PHP获得客户端的session d,然后跟随session d到指定的session文件保存目录中找到相应的session文件,不存在则创建,最后将数据序列化之后写入文件。读取session数据是类似的流程,对读出来的数据需要进行解序列化,生成相应的session变量

      用户在浏览网页时,用户的session会话开始,如果客户端的浏览器支持cookie,将会建立一个session的id到cookie,这个唯一的id由PHP随机生成,并且使用md5加密。客户端的cookie应该叫做session cookie,因为这个cookie是不会写入客户端的硬盘中,当一个session期结束的时候,该cookie也自动删除,如果客户端的浏览器禁用cookie,session的id将会放入URL中进行传递.
session id内存放着用户的相关信息,如:用户认证、认证时间、用户权限和其他信息。session文件的结构为: 变量名|类型:长度:值,多个变量用逗号分割

二、Session的存储目录

session是以文本文件形式保存在一个文件中sess_*,linux下默认存储在/tmp文件夹下,window默认存储在c:\windows\Temp下或C:\Documents and Settings\kuco\Local Settings\Temp\下。

PHP配置文件中session.save_path负责session文件的存放位置。如果没有配置则不会生成session文件,如果配置的目录session.save_path = "E:/ttt"不存在,则会报错:

Warning: session_start() [function.session-start]: open(E:/ttt\sess_e0b64760c92422d81c1d6202b66884f6, O_RDWR) failed: No such file or directory (2) in E:\APMServ5.2.6\www\htdocs\session\index.php on line 13
 Warning: Unknown: open(E:/ttt\sess_e0b64760c92422d81c1d6202b66884f6, O_RDWR) failed: No such file or directory (2) in Unknown on line 0
 Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (E:/ttt) in Unknown on line 0

 

所以,如果需要生成session文件,需要检查配置文件.如果没有配置目录,则请将php.ini中的“;session.save_path = "/tmp"” 改为“session.save_path = "E:/yourdir"”,并切记在E盘根目录下新建,名为yourdir的文件夹。设置完成后需要重启服务,然后设置生效。可以再php文件中测试,是否设置成功。

 

    $sessionpath = session_save_path();
    if (strpos ($sessionpath, ";") !== FALSE)
      $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1);

    //获取当前session的保存路径
    echo $sessionpath;


如果服务器是远程连接的,不在本地,不方便更改php配置文件,可以再程序中重置session目录,要想改变SESSION存储路径,必须在SESSION启动之前使用改变SESSION路径函数。。

   session_save_path("D:/www/session/rrr");
   session_start();


附:通过phpinfo()资料的获取也可以得到session文件的位置如:

session.cache_expire为session缓存在客户端的过期时间(适用于那种更新较少的网站使用)

session.gc_maxlifetime为session的有效期(默认为1440秒即24分钟,也就是说,客户端超过24分钟没有刷新,当前session就会失效。)

session.save_path为session在服务端的存储路径

三、Session的垃圾回收机制

    本节原本打算讲session的有效时间(或生命周期session lifetime),但是,如果理解了session的垃圾回收机制,那么对session的生命周期自然就明白了。

    1、session“回收”何时发生?

    由于PHP的工作机制,她并没有一个daemon线程,来定时的扫描session信息并判断其是否失效。当一个有效请求发生时,PHP会根据全局变量 session.gc_probability/session.gc_divisor(同样可以通过php.ini或者ini_set()函数来修改) 的值,来决定是否启动一个GC(Garbage Collector 中文意思是:垃圾收集工,即垃圾回收)。默认情况下,session.gc_probability = 1,session.gc_divisor =1000,也就是说有0.1%的可能性会启动GC,也就是说是否启动GC是由一个概率计算得来的,每1000个用户调用session_start()的时候,就有一个会执行一次垃圾回收机制将磁盘上的session文件删除。 

    注意:一般对于一些大型的门户网站,建议将session.gc_divisor调大一点,减少开销,接下来,我通过一个例子演示下,如何配置才能让调用gc(垃圾回收)进程呢!

通过配置php.ini文件,修改以下几个信息:

■ session.gc_maxlifetime = 60//当session文件在60s后还没有被访问的话,则该session文件将会被视为“垃圾文件”,并且等待gc(垃圾回收)进程的调用的时候被清理掉

■ session.gc_probability = 1000

因为gc进程被调用的概率是通过gc_probability/gc_divisor 计算得来的,这里我将session.gc_probability改成1000,而session.gc_divisor 默认情况下也是1000。则gc进程在每次执行session_start()函数的时候都会被调用到。

以下我通过截图简单的说明下:

 

 

      我开启三个会话,则创建三个对应的session文件,当每个文件在30秒内都没被调用的话,就会被当成是“垃圾文件”,等到gc进程调用的时候,“垃圾文件”就会被unlink,因为之前我已经通过修改php.ini配置文件,将gc被调用的概率改成百分百,所以接下来,如果我重新使用任何一个浏览器刷新下页面的时候,三个session文件,应该只剩下一个了

     2、session过期,但session文件未被删除

    这种情况是经常存在的,尤其是对于PHP技术开发人员,如下面的陈述:

    某网站的会员登录网站后,24分钟内都没有刷新,但是24分钟后仍然可以访问,而不要求输入账号、密码重新登录。

这个现象就是session过期,但session文件未被删除,即session过期,GC未启动删除session文件,此时session文本文件将被重新修改,即session文件的“最后修改时间”变化了,但session文件内容仍然没变化,在PHP环境中我们可以通过$_SESSION全局变量获取到

四、Session的存储优化

    session的存储机制,默认的是保存在files中,即以文件的方式保存session数据,在php中主要根据php.ini的配置session.save_handler来选择保存session的方式。如果并发访问很大,或者说session建立太多,目录下存在大量session文件,同一目录下文件数过多导致I/O性能下降,并且可能导致受到攻击最终出现文件系统错误。所以必须对其优化

    1、多级目录保存session文件

php.ini里面Session设置部分中有这样一项:

session.save_path = "N;MODE;/path"

    这项设置提供给我们可以给session存放目录进行多级散列,其中“N”表示要设置的目录级数,“MODE”表示目录的权限属性,默认为600,在WINDOWS上基本是不用设置的,*NIX上也可以不用设置,后面的“/path”表示session文件存放的根目录路径,比如我们设置为下面的格式

session.save_path = "2;/tmp/phpsession"


       上面的设置表示我们把/tmp/phpsession目录作为php的session文件存放根目录,在该目录下进行两级目录散列,每一级目录分别是0-9和a-z共36个字母数字为目录名,这样存放session的目录可以达到36*36个,相信作为单台服务器来说,这是完全够用了,如果说您的系统架构设计为多台服务器共享session数据,可以把目录级增加到3级或者更多。
  需要注意的是,php自己并不会自动创建子目录,需要您自己动手去创建,网上找到这样的自动创建目录的代码,大家可以做个参考。下面的代码自动创建3级子目录,可以自己动手根据需要进行修改。或者使用ext/session目录下的mod_files.sh脚本创建。

<?php
set_time_limit(0);
$string = '0123456789abcdefghijklmnopqrstuvwxyz';
$length = strlen($string);
function makeDir($param)
{
    if(!file_exists($param)) {
        makeDir(dirname($param));
        mkdir($param);
    }
} 
for($i = 0; $i < $length; $i++) {
    for($j = 0; $j < $length; $j++) {
        for($k = 0; $k < $length; $k++) {
            makeDir($string[$i].'/'.$string[$j].'/'.$string[$k]);
        }
    }
}
?>

值得注意的是:当N>0时自动垃圾回收将会失效,这个事后要建立一个自动回收机制,本文不做讲述了。
 

   2、保存到数据库

        见 session存储优化之基于mysql的存取

(未完,待续)

 

参考文档:

1、PHP session详解

2、深入理解session过期机制

PHP视频教程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章