Re: php 的 session 運作流程,實驗觀察心得
關於session運作原理, 我曾看過某部落格寫得還挺詳細的, 不過一來是
簡體中文, 二來遣詞用字的習慣跟台灣頗有差異, 所以要看完得多點耐心.
一回生, 二回熟, 看熟後對於session運作原理的觀念肯定有提升.
http://blog.chinaunix.net/u/27731/showart_259031.html
這篇文章應該是原文, 但也可能不知從哪裏轉來的, 大陸的論壇只要一有好
文, 馬上就被轉貼到到處都是.(這也算是各網站搞流量惡性競爭的關係吧?)
是不是原文就恕不進一步求證了.
這邊我再補充幾點:
1 關於php將 $_SESSION變數的內容 寫入session檔案(或是資料庫)的時機:
程設師若不在程式當中採用session_write_close()函式處理的話, 那麼
預設情況就是php script執行完後(包括以exit方式結束), php會自動寫入
session檔案(當然前提是session要有開).
1.1 session可以檔案形式儲存, 也可存在程設師所自定的媒體(包括資料庫)
當中, 這是session較進階的應用, 可參考 session_set_save_handler
, 以下不特別強調的話, 皆以session檔案為討論對象.
2 作業系統記錄session檔案被'讀寫'的時刻:
2.1 php在session初始化(呼叫session_start(),或是在組態中設定自動開啟session)
時, 會自session檔案將存在裏面的那串資料透過"解序列化"(unserialization)
程序, 將之載入$_SESSION變數. 這時候, 作業系統會修改session檔案被存取的
時刻.
2.2 php在php script終結前, 會將$_SESSION變數的內容透過"序列化"(serialization)
程序, 轉成一段文本字串(格式跟json很像), 寫入session檔案. 這時候, 作業
系統也會修改session檔案的存取時刻.
3 判斷session檔案是否過期的根據:
以上 2 要特意提出來, 正是因為session檔案被'讀寫'的時刻, 就是後來php用來
判斷session檔案是否過期的根據. 判斷方式為:
3.1 session檔案最近被讀寫的時刻 + maxlifetime(單位為秒) < 目前時刻
符合上述3.1 所描述者, 代表這session檔案已過期. 否則, 代表未過期.
用戶掛網不動, 自然不會觸動php script, 也就不會觸動php去讀寫其所屬
的session檔, 作業系統也就不會更新session檔被修改的時刻. 直到php要
做gc(garbage collection, 垃圾回收, 簡寫為 gc.)時, 就對所有的
session檔逐個套公式檢查是否過期? 可以想像一下, 如果這是一個萬人
大站, php要做gc是多麼耗費資源的事, 所以才必須以機率方式
(用gc_probability及gc_divisor控制發生頻率)來做. 另外, 檔案的讀寫
效能受限於作業系統, 所以又提供 session_set_save_handler 讓程設師
可以自訂session的儲存方式: 包括'開啟','關閉','讀取','寫入','摧毁',
'垃圾回收'的行為定義. (session的實作, 在大型網站上, 用資料庫是優
於檔案的, 甚至是用記憶體來存session.)
4 關於過期session的處理機制:
原則上, 用gc_probability, 以及gc_divisor這兩個組態變數來控制發生頻率
是沒錯. (其實這也很容易理解, 就相當於用rand(1,gc_divisor)取值, 只要
這個值小於等於gc_probaility, 就bingo了.) 它的運作原理是:
4.1 用戶觸動一個php script, 這個php script要有進行session初始化(就是將
session檔案的內容載入到$_SESSION變數)的動作才算.
4.2 session初始化完成後, 緊接著php根據gc_probability及gc_divisor所設定
機率來決定要不要進行gc的動作? 若要清除的話, 則砍除掉所有過期的
session檔案.
大致上是這樣, 不過session的運作機制若不花點時間還真的有點不太好搞懂. 有
時候run起來'很奇怪', 多半就是運作上的盲點, 或是還有些細節沒弄清楚.
最後貼段程式短碼讓大家玩玩, 看看心裏所想跟實際行為是否一致? :)
<?php
//執行gc的機率100%, session檔案只要1秒沒動就算逾期.
ini_set("session.gc_probability",1);
ini_set("session.gc_divisor", 1);
ini_set("session.gc_maxlifetime", 1);
session_start();
if(empty($_SESSION['i'])):
$_SESSION['i']=0;
endif;
$_SESSION['i']+=1;
echo $_SESSION['i'];
?>
※ 引述《megabio (LifeIsKuso)》之銘言:
: 一切是從這段話開始的 :
: "為什麼我修改了 gc_maxlifetime,還是沒有因為閒置而登出呢?"
(中略)
: ★★★ 我們可以想像一下 PHP 的處理過程 ★★★
: (1) browser A,B,C 都連上 server 開啟 session
: 並且在 server 的 "/tmp" 當中留下 sess_* 檔案
: (2) 30 過後,browser A 先重整頁面,對 server 提出連線要求
: (3) server 檢查 "/tmp" 底下的 sess_* 發現有 browser A 的 session id
: 於是判定這個 sess_* 不是垃圾
: (4) 至於其他的 sess_*,由於都已經超過 gc_maxlifetime 沒上來連線
: 所以這些 sess_* 有 100% 的機率被當成垃圾處理
※ 編輯: bobju 來自: 58.115.151.184 (11/17 10:42)
討論串 (同標題文章)