2008年6月25日 星期三

PHP中的session有效期問題

http://translate.google.com.tw/translate?hl=zh-TW&sl=zh-CN&u=http://www.bloggern.com/3325.html&sa=X&oi=translate&resnum=8&ct=result&prev=/search%3Fq%3Dsession.gc_probability%26complete%3D1%26hl%3Dzh-TW%26sa%3DG%26pwst%3D1

滿好懂的

PHP中的session有效期問題

PHP中的session有效期默认是1440秒(24分钟),也就是说,客户端超过24分钟没有刷新,当前session就会失效。 PHP中的session有效期默認是1440秒(24分鐘),也就是說,客戶端超過24分鐘沒有刷新,當前session就會失效。 很明显,这是不能满足需要的。很明顯,這是不能滿足需要的。
我在前几年就遇到过这个问题,在网上一搜,出来的结果千奇百怪。我在前幾年就遇到過這個問題,在網上一搜,出來的結果千奇百怪。 有的说要设置“session_life_time”(我没有找到这个参数),有的说要调用session_set_cookie_params,有的说要设置session.cookie_lifetime,有的说要调用session_cache_expire。有的說要設置“session_life_time”(我沒有找到這個參數),有的說要調用session_set_cookie_params,有的說要設置session.cookie_lifetime,有的說要調用session_cache_expire。 但这些方法没有一个管用。但這些方法沒有一個管用。
一个已知管用的方法是,使用session_set_save_handler,接管所有的session管理工作,一般是把session信息存储到数据库,这样可以通过SQL语句来删除所有过期的session,精确地控制session的有效期。一個已知管用的方法是,使用session_set_save_handler,接管所有的session管理工作,一般是把session信息存儲到數據庫,這樣可以通過SQL語句來刪除所有過期的session,精確地控制session的有效期。 这也是基于PHP的大型网站常用的方法。這也是基於PHP的大型網站常用的方法。 但是,一般的小型网站,似乎没有必要这么劳师动众。但是,一般的小型網站,似乎沒有必要這麼勞師動眾。
在PHP的文档中明确指出,设定session有效期的参数是session.gc_maxlifetime。在PHP的文檔中明確指出,設定session有效期的參數是session.gc_maxlifetime。 可以在php.ini文件中,或者通过ini_set()函数来修改这一参数。可以在php.ini文件中,或者通過ini_set()函數來修改這一參數。 问题在于,经过多次测试,修改这个参数基本不起作用,session有效期仍然保持24分钟的默认值。問題在於,經過多次測試,修改這個參數基本不起作用,session有效期仍然保持24分鐘的默認值。
由于这个问题并不是特别重要,当时也没有继续研究下去。由於這個問題並不是特別重要,當時也沒有繼續研究下去。 一直到前不久才彻底搞明白。一直到前不久才徹底搞明白。
由于PHP的工作机制,它并没有一个daemon线程,来定时地扫描session信息并判断其是否失效。由於PHP的工作機制,它並沒有一個daemon線程,來定時地掃描session信息並判斷其是否失效。 当一个有效请求发生时,PHP会根据全局变量session.gc_probability/session.gc_divisor(同样可以通过php.ini或者ini_set()函数来修改)的值,来决定是否启动一个GC(Garbage Collector)。當一個有效請求發生時,PHP會根據全局變量session.gc_probability/session.gc_divisor(同樣可以通過php.ini或者ini_set()函數來修改)的值,來決定是否啟動一個GC(Garbage Collector)。 默认情况下,session.gc_probability = 1,session.gc_divisor =100,也就是说有1%的可能性会启动GC。默認情況下,session.gc_probability = 1,session.gc_divisor =100,也就是說有1%的可能性會啟動GC。
GC的工作,就是扫描所有的session信息,用当前时间减去session的最后修改时间(modified date),同session.gc_maxlifetime参数进行比较,如果生存时间已经超过gc_maxlifetime,就把该session删除。 GC的工作,就是掃描所有的session信息,用當前時間減去session的最後修改時間(modified date),同session.gc_maxlifetime參數進行比較,如果生存時間已經超過gc_maxlifetime,就把該session刪 除。
到此为止,工作一切正常。到此為止,工作一切正常。 那为什么会发生gc_maxlifetime无效的情况呢?那為什麼會發生gc_maxlifetime無效的情況呢?
在默认情况下,session信息会以文本文件的形式,被保存在系统的临时文件目录中。在默認情況下,session信息會以文本文件的形式,被保存在系統的臨時文件目錄中。 在Linux下,这一路径通常为\tmp,在Windows下通常为C:\Windows\Temp。在Linux下,這一路徑通常為\tmp,在Windows下通常為C:\Windows\Temp。 当服务器上有多个PHP应用时,它们会把自己的session文件都保存在同一个目录中。當服務器上有多個PHP應用時,它們會把自己的session文件都保存在同一個目錄中。 同样地,这些PHP应用也会按一定机率启动GC,扫描所有的session文件。同樣地,這些PHP應用也會按一定機率啟動GC,掃描所有的session文件。
问题在于,GC在工作时,并不会区分不同站点的session。問題在於,GC在工作時,並不會區分不同站點的session。 举例言之,站点A的gc_maxlifetime设置为2小时,站点B的gc_maxlifetime设置为默认的24分钟。舉例言之,站點A的gc_maxlifetime設置為2小時,站點B的gc_maxlifetime設置為默認的24分鐘。 当站点B的GC启动时,它会扫描公用的临时文件目录,把所有超过24分钟的session文件全部删除掉,而不管它们来自于站点A或B。當站點B的GC啟動時,它會掃描公用的臨時文件目錄,把所有超過24分鐘的session文件全部刪除掉,而不管它們來自於站點A或B。 这样,站点A的gc_maxlifetime设置就形同虚设了。這樣,站點A的gc_maxlifetime設置就形同虛設了。
找到问题所在,解决起来就很简单了。找到問題所在,解決起來就很簡單了。 修改session.save_path参数,或者使用session_save_path()函数,把保存session的目录指向一个专用的目录,gc_maxlifetime参数工作正常了。修改session.save_path參數,或者使用session_save_path()函數,把保存session的目錄指向一個專用的目錄,gc_maxlifetime參數工作正常了。
严格地来说,这算是PHP的一个bug?嚴格地來說,這算是PHP的一個bug?
还有一个问题就是,gc_maxlifetime只能保证session生存的最短时间,并不能够保存在超过这一时间之后session信息立即会得到删除。還有一個問題就是,gc_maxlifetime只能保證session生存的最短時間,並不能夠保存在超過這一時間之後session信息立即會得到刪除。 因为GC是按机率启动的,可能在某一个长时间内都没有被启动,那么大量的session在超过gc_maxlifetime以后仍然会有效。因為GC是按機率啟動的,可能在某一個長時間內都沒有被啟動,那麼大量的session在超過gc_maxlifetime以後仍然會有效。 解决这个问题的一个方法是,把session.gc_probability/session.gc_divisor的机率提高,如果提到100%,就会彻底解决这个问题,但显然会对性能造成严重的影响。解決這個問題的一個方法是,把session.gc_probability/session.gc_divisor的機率提高,如果提到100%,就會徹底解決這個問題,但顯然會對性能造成嚴重的影響。 另一个方法是自己在代码中判断当前session的生存时间,如果超出了gc_maxlifetime,就清空当前session。另一個方法是自己在代碼中判斷當前session的生存時間,如果超出了gc_maxlifetime,就清空當前session。

1 則留言:

匿名 提到...

這篇太好了, 我找了好幾天
session_set_cookie_params($lifetime);
用網路上的方法試了好久, 你的方法看起來可行, (感謝)馬上試試