PHP SESSION原理
session是在服務器端保持用戶會話數據的一種方法,對應的cookie是在客戶端保持用戶數據。HTTP協議是一種無狀態協議,服務器響應完後就失去了與浏覽器的聯系,cookie引入浏覽器,使得數據跨越頁面交換。
首先客戶端和服務器端建立一一聯系,每個客戶端都有一個唯一標識,這樣服務器才能識別出來。建議唯一標識的方法有兩種:cookie或者通過GET方式指定。默認配置的PHP使用session的時會建立一個名叫”PHPSESSID”的cookie(可以通過php.ini修改session.name值指定),如果客戶端禁用cookie,你也可以指定通過GET方式把session id傳到服務器(修改php.ini中session.use_trans_sid等參數)。
客戶端將session id傳遞到服務器,服務器根據session id找到對應的文件,讀取的時候對文件內容進行反序列化就得到session的值,保存的時候先序列化再寫入。
事實就是這樣,如果服務器不支持session或者你想自定義session,完全可以DIY,通過PHP的uniqid生成永不重復的session id,然後找個地方存儲session的內容即可,還可以把session存儲在MySQL數據庫中。
所謂的session其實就是客戶端一個session id,服務器端一個session file,新建session時,告訴服務器要生成一個cookie以及准備好session文件,要不然你的session內容怎麼存;讀取session時告訴服務器,趕緊根據session id把session文件反序列化。
session影響系統性能
session在大訪問量網站上確實影響系統性能,影響性能的原因之一由文件系統設計造成,在同一個目錄下超過10000個文件時,文件的定位將非常耗時,PHP支持session目錄hash,我們可以通過修改php.ini中session.save_path =“2;/path/to/session/dir”,那麼session將存儲在兩級子目錄中,每個目錄有16個子目錄[0~f],不過好像PHPsession不支持創建目錄,你需要事先把那麼些目錄創建好 。
還有一個問題就是小文件的效率問題,一般我們的session數據都不會太大(1~2K),如果有大量這樣1~2K的文件在磁盤上,IO效率肯定會很差。可以通過緩存memcache和mysql數據庫提供效率。
session的同步
前端可能有很多台服務器,用戶在A服務器上登錄了,種下了session信息,然後訪問網站的某些頁面沒准跳到B服務器上去了,如果這個時候B服務器上沒有session信息又沒有做特殊處理,可能就會出問題了。
session同步有很多種,如果你是存儲在memcached或者MySQL中,那就很容易了,指定到同樣的位置即可,如果是文件形式的,你可以用NFS統一存儲。
(NFS是Network File System的簡寫,即網絡文件系統.網絡文件系統是FreeBSD支持的文件系統中的一種,也被稱為NFS. NFS允許一個系統在網絡上與他人共享目錄和文件。通過使用NFS,用戶和程序可以像訪問本地文件一樣訪問遠端系統上的文件。)
還有一種方式是通過加密的cookie來實現,用戶在A服務器上登錄成功,在用戶的浏覽器上種上一個加密的cookie,當用戶訪問B服務器時,檢查有無session,如果有當然沒問題,如果沒有,就去檢驗cookie是否有效,cookie有效的話就在B服務器上重建session。這種方法其實很有用,如果網站有很多個子頻道,服務器也不在一個機房,session沒辦法同步又想做統一登錄那就太有用了。
當然還有一種方法就是在負載均衡那一層保持會話,把訪問者綁定在某個服務器上,他的所有訪問都在那個服務器上就不需要session同步了。
<?php
session_start();
if(isset($_SESSION['test_sess'])){
$_SESSION['test_sess']++;
}else{
$_SESSION['test_sess'] = 0;
}
echo$_SESSION['test_sess'];
?>;
第一次請求服務器:
GET/test.php HTTP/1.1
Accept:*/*
Referer:http://localhost/
Accept-Language:zh-cn
Accept-Encoding:gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE6.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 1.1.4322)
Host:localhost
Connection:Keep-Alive
服務器第一次返回:
HTTP/1.1200 OK
Date:Fri, 26 Aug 2005 07:44:22 GMT
Server:Apache/2.0.54 (Win32) SVN/1.2.1 PHP/5.0.4 DAV/2
X-Powered-By:PHP/5.0.4
Set-Cookie:PHPSESSID=bmmc3mfc94ncdr15ujitjogma3; path=/
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache,must-revalidate, post-check=0, pre-check=0
Pragma:no-cache
Content-Length:1
Keep-Alive:timeout=15, max=99
Connection:Keep-Alive
Content-Type:text/html; charset=utf-8
Content-Language:Off
第二次請求服務器:
GET/test.php HTTP/1.1
Accept:*/*
Referer:http://localhost/
Accept-Language:zh-cn
Accept-Encoding:gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE6.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 1.1.4322)
Host:localhost
Connection:Keep-Alive
Cookie: PHPSESSID=bmmc3mfc94ncdr15ujitjogma3
服務器第二次返回:
HTTP/1.1200 OK
Date:Fri, 26 Aug 2005 07:44:23 GMT
Server:Apache/2.0.54 (Win32) SVN/1.2.1 PHP/5.0.4 DAV/2
X-Powered-By:PHP/5.0.4
Set-Cookie:PHPSESSID=bmmc3mfc94ncdr15ujitjogma3; path=/
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache,must-revalidate, post-check=0, pre-check=0
Pragma:no-cache
Content-Length:1
Keep-Alive:timeout=15, max=98
Connection:Keep-Alive
Content-Type:text/html; charset=utf-8
Content-Language:Off
仔細對比這些輸出,第二次請求比第一次請求多出來的就是:
Cookie:PHPSESSID=bmmc3mfc94ncdr15ujitjogma3
這個header將會向服務器發送一個cookie信息,告訴服務器我有一個cookie,名字叫PHPSESSID,內容是bmmc3mfc94ncdr15ujitjogma3。
這個cookie是怎麼來的呢?看第一次服務器返回的信息裡邊有:
Set-Cookie:PHPSESSID=bmmc3mfc94ncdr15ujitjogma3; path=/
這是服務器向客戶端浏覽器寫一個cookie,名字是PHPSESSID,值是bmmc3mfc94ncdr15ujitjogma3,這個值實際就是所謂的session_id。
繼續看第二次向服務器發出的請求,仍然向服務器發送了PHPSESSID這個cookie
可以得到以下結論:
1、只要使用了session,就會通過cookie的方式向客戶端浏覽器發送session
2、每次向服務器發出請求的時候,本地浏覽器會把cookie附帶在請求信息中
COOKIE
cookie 是一種在遠程浏覽器端儲存數據並以此來跟蹤和識別用戶的機制。
PHP在http 協議的頭信息裡發送cookie,因此setcookie()函數必須在其它信息被輸出到浏覽器前調用。
原理.
a.服務器通過隨著響應發送一個http 的Set-Cookie 頭,在客戶機中設置一個cookie(多個cookie 要多個頭)。
b.客戶端自動向服務器端發送一個http 的cookie 頭,服務器接收讀取。
HTTP/1.x 200 OK
X-Powered-By: PHP/5.2.1
Set-Cookie:TestCookie=something from somewhere; path=/
Expires: Thu, 19 Nov 2007 18:52:00 GMT
Cache-Control: no-store, no-cache,must-revalidate, post-check=0,pre-check=0
Pragma: no-cache
Content-type: text/html
這一行實現了cookie 功能,收到這行後
Set-Cookie: TestCookie=something fromsomewhere; path=/
浏覽器將在客戶端的磁盤上創建一個cookie 文件。
下面的同樣的效果:
setcookie('TestCookie','something from somewhere','/');
header('Set-Cookie:TestCookie=something from somewhere; path=/')
常見問題解決:
1) 用 setcookie()時有錯誤提示,可能是因為調用setcookie()前面有輸出或空格。
2) $_COOKIE 受magic_quotes_gpc 影響,可能自動轉義。
3) 使用的時候,有必要測試用戶是否支持cookie。
下面以用戶登錄為例分析session和cookie
HTTP協議是一種無狀態協議,服務器響應完用戶的請求,就失去了與浏覽器的聯系,PHP是如何實現session的。
用戶第一次訪問服務器時,因為沒有session信息,需要登錄驗證,用戶通過表單把用戶名,密碼,驗證碼等信息提交給服務器,服務器在驗證用戶的合法性之前先對數據進行預處理。通過到數據庫驗證,用戶是合法的,這個時候服務器會給浏覽器信息中包含Set-Cookie: PHPSESSID=bmmc3mfc94ncdr15ujitjogma3;這樣的信息,這樣浏覽器會把信息寫到本地文件中,其中PHPSESSID為唯一標識符。同時服務器也會在指定的文件把序列化的session信息保存在文件中。當用戶再次請求時,浏覽器會把對應cookie中的PHPSESSID也發送給服務器,服務器得到PHPSESSID,會到session文件中驗證,如果驗證成功,就直接登錄。從而類似的可以實現數據在不同用戶頁面之前的傳遞。session中的值是key-value。
session影響系統性能
session在大訪問量網站上確實影響系統性能,影響性能的原因之一由文件系統設計造成,在同一個目錄下超過10000個文件時,文件的定位將非常耗時,PHP支持session目錄hash,我們可以通過修改php.ini中session.save_path =“2;/path/to/session/dir”,那麼session將存儲在兩級子目錄中,每個目錄有16個子目錄[0~f],不過好像PHPsession不支持創建目錄,你需要事先把那麼些目錄創建好 。
還有一個問題就是小文件的效率問題,一般我們的session數據都不會太大(1~2K),如果有大量這樣1~2K的文件在磁盤上,IO效率肯定會很差。可以通過緩存memcache和mysql數據庫提供效率。
session的同步
前端可能有很多台服務器,用戶在A服務器上登錄了,種下了session信息,然後訪問網站的某些頁面沒准跳到B服務器上去了,如果這個時候B服務器上沒有session信息又沒有做特殊處理,可能就會出問題了。
session同步有很多種,如果你是存儲在memcached或者MySQL中,那就很容易了,指定到同樣的位置即可,如果是文件形式的,你可以用NFS統一存儲。
(NFS是Network File System的簡寫,即網絡文件系統.網絡文件系統是FreeBSD支持的文件系統中的一種,也被稱為NFS. NFS允許一個系統在網絡上與他人共享目錄和文件。通過使用NFS,用戶和程序可以像訪問本地文件一樣訪問遠端系統上的文件。)
還有一種方式是通過加密的cookie來實現,用戶在A服務器上登錄成功,在用戶的浏覽器上種上一個加密的cookie,當用戶訪問B服務器時,檢查有無session,如果有當然沒問題,如果沒有,就去檢驗cookie是否有效,cookie有效的話就在B服務器上重建session。這種方法其實很有用,如果網站有很多個子頻道,服務器也不在一個機房,session沒辦法同步又想做統一登錄那就太有用了。
還有一種方法就是在負載均衡那一層保持會話,把訪問者綁定在某個服務器上,其他所有訪問都在那個服務器上就不需要