|
楼主 |
发表于 2010-1-6 11:36:26
|
显示全部楼层
本帖最后由 ares333 于 2010-1-9 22:23 编辑
查阅了很多资料和做了很多测试,能得到这高认可,激动心情无法言语表达。
上面是没有使用数据库的情形,使用数据库后有一个回收问题,CI_Session的_sess_gc()函数。
此函数是在CI_Session的构造函数中最后调用的,如果配置文件中sess_expiration设为0之后,那数据库中存储的session岂不是要两年之后才回收?(因为config文件中设为零,CI会认为这是两年)
解决此问题有两种方法,第一,覆盖CI_Session(就是重写,但是只修改部分代码),第二,就是扩展原有类,覆盖_sess_gc()函数,然后在扩展后的构造函数中调用一次_sess_gc()。
第一种方法条理不如第二种方法清晰,第二种方法相对整体的性能损失几乎为零。所以采用第二种方法:
config.php中添加一项配置(并且增加了几行注释)
PHP复制代码
/*
|--------------------------------------------------------------------------
| Session Variables
|--------------------------------------------------------------------------
|
| 'session_cookie_name' = the name you want for the cookie
| 'encrypt_sess_cookie' = TRUE/FALSE (boolean). Whether to encrypt the cookie
| 'session_expiration' = the number of SECONDS you want the session to last.
| by default sessions last 7200 seconds (two hours). Set to zero for no expiration.
| 'time_to_update' = how many seconds between CI refreshing Session Information
| 'sess_lifetime' = session lifetime in database,only work when sess_expiration = 0
|
| caution:
| if sess_expiration = 0 , 'sess_time_to_update' < 'sess_lifetime'
|
*/
$config['sess_cookie_name'] = 'session';
$config['sess_expiration'] = 0;
$config['sess_encrypt_cookie'] = TRUE;
$config['sess_use_database'] = TRUE;
$config['sess_table_name'] = 'sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_match_useragent'] = TRUE;
$config['sess_time_to_update'] = 0.1*60*60;
$config['sess_lifetime'] = 0.5*60*60;
复制代码
当sess_expiration = 0 时,sess_lifetime 才起作用,而且'sess_time_to_update' 必须小于'sess_lifetime'
系统运行过程中不能动态改变sess_expiration 的值(也就是说只能在配置文件中一次性定义sess_expiration的值),否则session相关的操作会出现不可预料的结果
X_Session.php
PHP复制代码 <?php
class X_Session extends CI_Session {
function X_Session (){
parent ::CI_Session();
$this->sess_expiration = $this->CI->config->item('sess_expiration');
$this->_sess_gc ();
}
function _set_cookie ($cookie_data = NULL)
{
if (is_null($cookie_data))
{
$cookie_data = $this->userdata;
}
// Serialize the userdata for the cookie
$cookie_data = $this->_serialize ($cookie_data);
if ($this->sess_encrypt_cookie == TRUE)
{
$cookie_data = $this->CI->encrypt->encode($cookie_data);
}
else
{
// if encryption is not used, we provide an md5 hash to prevent userside tampering
$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
}
// Set the cookie
//sess_expiration = 0 means the cookie won't be effective after the browser closed
if($this->sess_expiration == 0)
$expiration = 0;
else
$expiration = $this->sess_expiration + time();
setcookie(
$this->sess_cookie_name,
$cookie_data,
$expiration,
$this->cookie_path,
$this->cookie_domain,
0
);
}
function _sess_gc ()
{
if ($this->sess_use_database != TRUE)
{
return;
}
srand(time());
if ((rand() % 100) < $this->gc_probability)
{
if($this->sess_expiration == 0){
$expiration = $this->CI->config->item('sess_lifetime');
}else{
$expiration = $this->sess_expiration;
}
$expire = $this->now - $expiration;
$this->CI->db->where("last_activity < {$expire}");
$this->CI->db->delete($this->sess_table_name);
log_message ('debug', 'Session garbage collection performed.');
}
}
} 复制代码
_sess_gc()调用了之后不一定会执行(看源代码就会明白),会有一定概率执行(这个概率可以调节),所以如果你的网站配置文件sess_expiration设为0,会产生很多无用的数据项(因为如果sess_expiration不为零,回收时间就是sess_expiration的值,否则是两年),这时候就需要根据实际情况在X_Session中重写_sess_gc()函数调节概率(过高会加重数据库查询负担),这时候回收的概率就是CI系统默认概率+调节后的概率(因为调用了两次回收函数)。这样可以减少session表的体积(尤其对有大量用户的系统)。
把回收概率改为100%简单测试了一下没有问题,希望能给点意见。
参考了一篇文章得到了一些启发。
怕原文失效就贴了过来
http://weblog.kreny.com/2007/06/_php_session_time_out.html
关于 PHP Session 的 Time out 和有效设置 Session 时间限制的一些小结
一直搞不清楚 PHP 里面关于 Session Time Out 的时间控制,这里稍微总结一下,做个纪录。
php.ini 的关于 Session 的设置
① 是否要 cache ? ==> NO!
在 http 的 header 里面输出一些关于 session 的 cache(??对具体定义不是很清除) ,虽然这个和 Session 的time out 没有什么直接的联系,但从历来的经验来讲,会出现一些很奇怪的现象,所以这里统一将 cache 关闭,使得 header里面不出现 session 的信息。 ; Set to {nocache,private,public,} to determine HTTP caching aspects
; or leave this empty to avoid sending anti-caching headers.
session.cache_limiter = nocache
; Document expires after n minutes.
session.cache_expire = 180 ② 关于 garbage
; Define the probability that the 'garbage collection' process is started
; on every session initialization.
; The probability is calculated by using gc_probability/gc_divisor,
; e.g. 1/100 means there is a 1% chance that the GC process starts
; on each request.
session.gc_probability = 1
session.gc_divisor = 100
; After this number of seconds, stored data will be seen as 'garbage' and
; cleaned up by the garbage collection process.
session.gc_maxlifetime = 1440
这个设置是指:在 1440 秒后, Session 会被认作是 garbage (垃圾),而针对这些garbage ,在每次连接(request),有 1/100 的几率(下文称为“清除几率”)来清除这些 garbage--当然,可以认为在1440 秒后,平均通过进行 100 次的连接就会将 garbage session清除。
如果将以上的分子和分母都调整到 1, 那所有的 garbage session 将会在期限(session.gc_maxlifetime)过后的第一次 request 的时候被清除。但是这会大大加大对服务器的负荷,不建议使用。
③最后设置 session.cookie_lifetime ; Whether to use cookies.
session.use_cookies = 1
; Lifetime in seconds of cookie or, if 0, until browser is restarted.
; session.cookie_lifetime 以秒数指定了发送到浏览器的 cookie 的生命周期。
; 值为 0 表示“直到关闭浏览器”。默认为 0。
session.cookie_lifetime = 0
如果使用 cookie 在客户端保存 session 信息,这里可以设置 cookie 的有效时间。自己的理解是,在客户端的 cookie 里面保存有 Session ID,用来比较其有效期限。
现在做的项目使用 cookie 来保存用户信息,每当 session.cookie_lifetime 设置为 0 的时候,理所当然 IE被关闭后每次都要重新登陆。但是当将此设置为一定的值后,在指定的秒数内重新打开该页面,仍旧保留以前的 cookie 数据并且自动登陆。留意点:
session.cookie_lifetime 设定为一个值 (例如 1000 秒)后,相应的 session.gc_maxlifetime 也需要设置为 1000 ,才能方便清除?
因为测试过几次,将 session.cookie_lifetime 设置为较小的数值(例如 100秒)的时候,即使将“清除几率”变为 100%, 也不能在这个较小数值的时间内将 session 清除。因此怀疑是 session.gc_maxlifetime设置了较大的一个值,使得系统需要更多的时间将 session 认定为 garbage, 即使这个 session 实质上已经过了它的lifetime。
几经测试,在统一了 session.cookie_lifetime 和 session.gc_maxlifetime 后, session 的定期清除效果比较好。
那么如何有效的限制 Session 的 Time out 呢?以下是两个众所周知的方案,仅作纪录。 - 将 Timestamp 放入 $_SESSION 中,每次 request 的时候对其值和现在值 now()进行比较。如果时间间隔没有超出 Time Out 的期限,那么就将现在的时间 now() 赋值给 $_SESSION 中的纪录timestamp 的部分。
- 将 timestamp 放入 数据库中,每次 request 的时候对其值和现在值 now() 进行比较。如果时间间隔没有超出 Time Out 的期限,那么就将该用户的 session 纪录进行更新。
|
|