摘要:而這個(gè)就是作為客戶端的唯一標(biāo)識(shí)而存在的即使在同一臺(tái)電腦上,瀏覽器和瀏覽器對(duì)于服務(wù)器來(lái)說(shuō)都是不同的客戶端。當(dāng)然,這個(gè)名不是固定的,我們可以在文件中的項(xiàng)進(jìn)行修改。文件的命名格式是的值。比如說(shuō),的值是,是一個(gè)長(zhǎng)度為的字符串。
前言:
在不久之前,本人去參加了某公司的實(shí)習(xí)面試,其中面試官問(wèn)我關(guān)于 SESSION 實(shí)現(xiàn)的原理,當(dāng)時(shí)我就懵逼了,因?yàn)樵谥暗拈_(kāi)發(fā)中,我只知道 session 與 cookie 的區(qū)別在于:session 是保存在服務(wù)器端,cookie 保存在客戶端。那 session 在服務(wù)端是怎么樣保存的?session_id 又是什么?等等。我當(dāng)時(shí)答不上來(lái)?;貋?lái)后決定把這些搞懂。
為什么要使用 SESSION?
是因?yàn)槟壳熬W(wǎng)絡(luò)中所使用的http協(xié)議造成的,http協(xié)議是無(wú)狀態(tài)協(xié)議,通俗點(diǎn)說(shuō)就是當(dāng)你發(fā)送一次請(qǐng)求道服務(wù)器端,然后再次發(fā)送請(qǐng)求到服務(wù)器端,服務(wù)器是不知道你的這一次請(qǐng)求和上一次請(qǐng)求是來(lái)源于同一個(gè)人發(fā)送的。而 session 就能很好解決這個(gè)問(wèn)題。
在我們的訪問(wèn)期間,各個(gè)頁(yè)面間共享的數(shù)據(jù)放在session中,就比如說(shuō)我們的登陸信息,如果沒(méi)有 session 的話,當(dāng)你在這個(gè)頁(yè)面登陸之后,在點(diǎn)擊下一個(gè)頁(yè)面的時(shí)候你需要再次登陸。
引入:
現(xiàn)在我們來(lái)看看平時(shí)我們是怎么使用 session 的,大家看下面的例子
現(xiàn)在我們?cè)跒g覽器 A 打開(kāi)
http://localhost/index.php?user=lsgogroup;返回:
array(1){["user"]=>string(9)"lsgogroup"}在瀏覽器 B 打開(kāi)
http://localhost/index.php返回:
array(1){["user"]=>string(7)"default"}問(wèn)題:
session_start() 的作用是什么?
為什么在瀏覽器 B 中返回的不是:array(1){[“user”]=>string(9)“l(fā)sgogroup”} ?$_SESSION 數(shù)組是怎么保存這些數(shù)據(jù)的?
理解 PHP SESSION 機(jī)制:
session 機(jī)制是一種服務(wù)器端的機(jī)制,服務(wù)器使用一種類似于散列表的結(jié)構(gòu)來(lái)保存信息。當(dāng)程序需要為某個(gè)客戶端的請(qǐng)求創(chuàng)建一個(gè) session 的時(shí)候,服務(wù)器首先檢查這個(gè)客戶端的請(qǐng)求(Http Request)里是否已包含了一個(gè) session 標(biāo)識(shí)-稱為 sessionid,如果已包含一個(gè) sessionid 則說(shuō)明以前已經(jīng)為此客戶端創(chuàng)建過(guò) session,服務(wù)器就按照 sessionid 把這個(gè) session 檢索出來(lái)使用,如果客戶端請(qǐng)求不包含 sessionid,則為此客戶端創(chuàng)建一個(gè) session 并且生成一個(gè)與此 session 相關(guān)聯(lián)的 sessionid,sessionid的值應(yīng)該是一個(gè)既不會(huì)重復(fù),又不容易被找到規(guī)律以仿造的字符串,這個(gè) sessionid 將被在本次響應(yīng)中返回給客戶端保存。而這個(gè) sessionid 就是作為客戶端的唯一標(biāo)識(shí)而存在的(即使在同一臺(tái)電腦上,瀏覽器 A 和瀏覽器 B 對(duì)于服務(wù)器來(lái)說(shuō)都是不同的客戶端)。
上面一段話你可能暫時(shí)不會(huì)理解,不過(guò)不要緊,我會(huì)在下面作出解釋:
現(xiàn)在我們來(lái)看看瀏覽器 A 和 瀏覽器 B 的 cookie:
瀏覽器 A (這里對(duì)應(yīng)是谷歌瀏覽器):
瀏覽器 B (這里對(duì)應(yīng)是火狐瀏覽器) :
對(duì)比可以看到,兩個(gè)瀏覽器對(duì)于 localhost 都有一條名為 PHPSESSID 的 cookie 記錄,而這個(gè) PHPSESSID 就是上面所說(shuō)的 sessionid,它告訴服務(wù)器請(qǐng)求是來(lái)自瀏覽器 A 還是瀏覽器 B 。
現(xiàn)在我們可以回答上面的問(wèn)題 2 了:
由于瀏覽器 A 的 PHPSESSID 和瀏覽器 B 的 PHPSESSID 是不一樣的,因此服務(wù)器根據(jù) sessionid 檢索 session 的數(shù)據(jù)也是不一樣的,也就是說(shuō)瀏覽器 A 請(qǐng)求的 $_SESSION 數(shù)組和 瀏覽器 B 請(qǐng)求的 $_SESSION 數(shù)組也是不一樣的。
(當(dāng)然,PHPSESSID 這個(gè) id 名不是固定的,我們可以在 php.ini 文件中的 session.name 項(xiàng)進(jìn)行修改。)
上面的例子是使用 COOKIE 保存 PHPSESSID,但是,由于 cookie 可以被人為的禁止,必須有其他機(jī)制以便在 cookie 被禁止時(shí)仍然能夠把 sessionid 傳遞回服務(wù)器。有兩種技術(shù)可以解決這個(gè)問(wèn)題:
URL重寫(xiě),就是把 sessionid 直接附加在URL路徑的后面:
http://localhost/index.php?user=lsgogroup&PHPSESSID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng隱藏表單傳遞。
由于這不是重點(diǎn),這里不展開(kāi)講。SESSION 是怎么存儲(chǔ)數(shù)據(jù)的?
答:session 是以文件的形式保存的。php.ini 中的配置項(xiàng) session.save_handler = files;
默認(rèn)為 file,定義 session 在服務(wù)端的保存方式,file 意為把 session 保存到一個(gè)臨時(shí)文件里。php.ini 中的配置項(xiàng) session.save_path= “”;
這個(gè)里面填寫(xiě)的路徑,將會(huì)使session文件保存在該路徑下。session 文件的命名格式是:“sess_[PHPSESSID的值]”。每一個(gè)文件,里面保存了一個(gè)會(huì)話的數(shù)據(jù)。
我們查看服務(wù)器端 session.save_path 目錄會(huì)發(fā)現(xiàn)很多類似 sess_vv9lpgf0nmkurgvkba1vbvj915 這樣的文件,這個(gè)其實(shí)就是 sessionid(也就是 PHPSESSID) “vv9lpgf0nmkurgvkba1vbvj915″ 對(duì)應(yīng)的數(shù)據(jù)。真相就在這里,客戶端將 sessionid 傳遞到服務(wù)器,服務(wù)器根據(jù) sessionid 找到對(duì)應(yīng)的文件,讀取的時(shí)候?qū)ξ募?nèi)容進(jìn)行反序列化就得到 session 的值($_SESSION數(shù)組中的數(shù)據(jù)),保存的時(shí)候先序列化再寫(xiě)入。
由于我做實(shí)驗(yàn)的時(shí)候使用的是 ubuntu 系統(tǒng),因此我的 session.save_path 默認(rèn)實(shí)在 /var/lib/php/sessions 下,我們來(lái)看看前面瀏覽器 A 生成的 session 文件是怎樣的(瀏覽器 A 的 PHPSESSID = ‘nqqleletmsb0nuf7d4ulvotk45’):
cd /var/lib/php/sessions #由于session數(shù)據(jù)是很重要的數(shù)據(jù),因此必須只能 root 用戶才能打開(kāi) sudo vim sess_nqqleletmsb0nuf7d4ulvotk45 #看看文件格式是不是 "sess_[PHPSESSID的值]"文件內(nèi)容:
user|s:9:"Jodieeeee";
從文件內(nèi)容可以看到,數(shù)據(jù)是經(jīng)過(guò)序列化的,數(shù)據(jù)的讀取規(guī)則是這樣的:
每一個(gè)session的值是以分號(hào)";"分開(kāi)的。比如”user|s:9:“Jodieeeee”;“就是一個(gè)完整的session值結(jié)束,如果再添加 $_SESSION[‘name’]=“LSGOZJ”,則變成這樣 ”user|s:9:“Jodieeeee”;name|s:7:“LSGOZJ”;“
里面的讀取規(guī)則:符號(hào)“|”前面表示 session 名稱。符號(hào)后面是該 session 的具體信息。包括:數(shù)據(jù)類型,字符長(zhǎng)度,內(nèi)容。比如說(shuō) ”user|s:9:“Jodieeeee”;“,$_SESSION[‘user’] 的值是 “Jodieeeee”,是一個(gè)長(zhǎng)度為 9 的字符串。
等等。。。
到了這里,我們就解決了上面的問(wèn)題 3 了。其實(shí)還有很多種存儲(chǔ)session的方式,如果我們想自定義別的方式保存(比如用數(shù)據(jù)庫(kù)),則需要把該項(xiàng)設(shè)置為 user;我們還可以使用 memcache、redis 等優(yōu)秀的緩存系統(tǒng)(前提是你的服務(wù)器安裝了此類軟件)。
session_start()函數(shù)的作用是什么?
了解的原理之后,所謂的 session 其實(shí)就是客戶端一個(gè) sessionid 對(duì)應(yīng)服務(wù)器端一個(gè) session file,新建session 之前執(zhí)行 session_start() 是告訴服務(wù)器要種一個(gè) cookie 以及準(zhǔn)備好 session 文件,要不然你的session 內(nèi)容怎么存;讀取 session 之前執(zhí)行 session_start() 是告訴服務(wù)器,趕緊根據(jù) sessionid 把對(duì)應(yīng)的 session 文件反序列化。說(shuō)白了,當(dāng)我們使用 php 的內(nèi)置函數(shù) session_start( ) 的時(shí)候,就是到服務(wù)器的指定的磁盤(pán)目錄把 session 數(shù)據(jù)載入,實(shí)際上就是拿類似 sess_74dd7807n2mfml49a1i12hkc45 的文件。
只有一個(gè) session 函數(shù)可以在 session_start() 之前執(zhí)行,session_name():讀取或指定 session 名稱(比如默認(rèn)的就是”P(pán)HPSESSID”),這個(gè)當(dāng)然要在session_start之前執(zhí)行。
根據(jù) http 的請(qǐng)求機(jī)制,當(dāng)瀏覽器請(qǐng)求的時(shí)候,頭部信息會(huì)把瀏覽器中的 cookie 一起發(fā)給服務(wù)器。PHPSESSID 這個(gè) cookie 也是在其中發(fā)給了服務(wù)器,php 引擎通過(guò)讀取 PHPSESSID 的值來(lái)確定要載入哪個(gè) session 文件。
比如值為 74dd7807n2mfml49a1i12hkc45,載入的就是"sess_74dd7807n2mfml49a1i12hkc45"。
注:當(dāng)你調(diào)用 php 的函數(shù) session_start(),才表明你需要使用 session 文件了。不然平白無(wú)故就去載入文件,浪費(fèi)性能。
SESSION 的清理:
在平時(shí)我們談?wù)?SESSION 的機(jī)制的時(shí)候,常常聽(tīng)到這樣一種誤解“只要關(guān)閉瀏覽器,session就消失了”(本人也是一度認(rèn)為這樣),其實(shí)可以想象一下會(huì)員卡的例子,除非顧客主動(dòng)對(duì)店家提出銷卡,否則店家絕對(duì)不會(huì)輕易刪除顧客的資料。對(duì) session 來(lái)說(shuō)也是一樣的,除非程序通知服務(wù)器刪除一個(gè) session,否則服務(wù)器會(huì)一直保留,程序一般都是在用戶做 logoff (注銷操作,類似于 session_destroy()操作)的時(shí)候發(fā)個(gè)指令去刪除 session。然而瀏覽器從來(lái)不會(huì)主動(dòng)在關(guān)閉之前通知服務(wù)器它將要關(guān)閉,因此服務(wù)器根本不會(huì)有機(jī)會(huì)知道瀏覽器已經(jīng)關(guān)閉,之所以會(huì)有這種錯(cuò)覺(jué),是大部分 session 機(jī)制都使用會(huì)話 cookie 來(lái)保存 sessionid ,而關(guān)閉瀏覽器后這個(gè)sessionid就消失了,再次連接服務(wù)器時(shí)也就無(wú)法找到原來(lái)的session,但是服務(wù)器上對(duì)應(yīng)的 session file 依然存在。
為什么關(guān)閉瀏覽器后 sessionid 就會(huì)消失呢?這跟 cookie 在客戶端的存儲(chǔ)有關(guān),如果在設(shè)置 cookie 的時(shí)候沒(méi)有指定生命周期,那么 cookie 的數(shù)據(jù)是存儲(chǔ)在內(nèi)存中的,當(dāng)瀏覽器被關(guān)閉,內(nèi)存被回收了,那么cookie 也就沒(méi)有了(這就是為什么cookie在沒(méi)有指定生命周期的時(shí)候,其生命周期與瀏覽器生命周期一樣)。
如果服務(wù)器設(shè)置的 cookie 被保存到硬盤(pán)上(設(shè)置了生命周期),或者使用某種手段改寫(xiě)瀏覽器發(fā)出的HTTP請(qǐng)求頭,把原來(lái)的 sessionid 發(fā)送給服務(wù)器,則再次打開(kāi)瀏覽器仍然能夠找到原來(lái)的session。
恰恰是由于關(guān)閉瀏覽器不會(huì)導(dǎo)致 session 被刪除,迫使服務(wù)器為 seesion 設(shè)置了一個(gè)失效時(shí)間,當(dāng)距離客戶端下一次使用 session 的時(shí)間超過(guò)這個(gè)失效時(shí)間時(shí),服務(wù)器就可以認(rèn)為客戶端已經(jīng)停止了活動(dòng),才會(huì)把session 刪除以節(jié)省存儲(chǔ)空間。
我們來(lái)看看服務(wù)器是怎樣刪除 session 數(shù)據(jù)的:
session.gc_probability = 1 session.gc_divisor = 100 session.gc_maxlifetime = 1440這三個(gè)配置項(xiàng)組合構(gòu)建服務(wù)端 session 的垃圾回收機(jī)制。
session.gc_probability 與 session.gc_divisor 構(gòu)成執(zhí)行 session 清理的概率,理論上的解釋為服務(wù)端定期有一定的概率調(diào)用 gc(garbage collection 垃圾回收) 進(jìn)程來(lái)對(duì) session 進(jìn)行清理,清理的概率為:gc_probability/gc_divisor 比如:1/100 表示每一個(gè)新會(huì)話初始化時(shí),有 1% 的概率會(huì)啟動(dòng)垃圾回收程序,清理的標(biāo)準(zhǔn)為 session.gc_maxlifetime 定義的時(shí)間(清理過(guò)期的數(shù)據(jù))。
我所用的系統(tǒng)是ubuntu,php.ini 中指定的 session.gc_probability = 0,也就是概率為零,原因是該系統(tǒng)是使用 cron 腳本來(lái)執(zhí)行垃圾清理的。
后話:
session 還有很多需要整理和學(xué)習(xí)的地方,如:session多服務(wù)器共享的問(wèn)題,假如有多臺(tái)php服務(wù)器進(jìn)行負(fù)載均衡的時(shí)候,用戶登錄時(shí)訪問(wèn)的是第一臺(tái)服務(wù)器,沒(méi)準(zhǔn)下一個(gè)頁(yè)面訪問(wèn)的是第二臺(tái)服務(wù)器,但是 session 數(shù)據(jù)是存儲(chǔ)在第一臺(tái)服務(wù)器上的,因此在訪問(wèn)下一個(gè)頁(yè)面的時(shí)候由于沒(méi)有 session 數(shù)據(jù)(第二臺(tái)服務(wù)器上)導(dǎo)致用戶必須重新登陸。
從上面的分析我們也知道,php 中 session 默認(rèn)通過(guò)文件的方式實(shí)現(xiàn),但是如果訪問(wèn)量大,可能產(chǎn)生的 SESSION 文件會(huì)比較多,從眾多的文件中選擇其中一個(gè)文件不是一件輕松的事情,而且每次都以打開(kāi)文件、讀取文件的方式,也會(huì)產(chǎn)生大量的 I/O 操作,嚴(yán)重影響服務(wù)器的性能。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31338.html
摘要:六原理說(shuō)明侵入編譯流程,在編譯過(guò)程中,修改庫(kù)的字節(jié)碼,修改解析相關(guān)的方法,在數(shù)據(jù)類型不一致的時(shí)候,跳過(guò)當(dāng)前字段的解析。 一、目錄 1.gson-plugin告別Json數(shù)據(jù)類型不一致(一)2.gson-plugin基礎(chǔ)源碼分析(二)3.gson-plugin深入源碼分析(三)4.gson-plugin如何在JitPack發(fā)布(四) 看完這4篇文章,對(duì)Gson解析會(huì)有更加深刻的認(rèn)識(shí),對(duì)A...
摘要:來(lái)自博客整理于面試別人或被別人面試的一些題目持續(xù)更新答案網(wǎng)上基本都有,不一一列舉。例有個(gè)人去游玩,需要買水,商店活動(dòng)買瓶贈(zèng)送一瓶。請(qǐng)問(wèn)題目至少需要買多少瓶飲料才可以人手一瓶前端方面前端性能團(tuán)隊(duì)總結(jié)的條黃金定律說(shuō)出幾條 來(lái)自 AT博客整理于面試別人或被別人面試的一些題目(持續(xù)更新),答案網(wǎng)上基本都有,不一一列舉。希望能幫到需要換工作的你。 數(shù)據(jù)庫(kù) mysql 索引的理解 mysql b...
摘要:來(lái)自博客整理于面試別人或被別人面試的一些題目持續(xù)更新答案網(wǎng)上基本都有,不一一列舉。例有個(gè)人去游玩,需要買水,商店活動(dòng)買瓶贈(zèng)送一瓶。請(qǐng)問(wèn)題目至少需要買多少瓶飲料才可以人手一瓶前端方面前端性能團(tuán)隊(duì)總結(jié)的條黃金定律說(shuō)出幾條 來(lái)自 AT博客整理于面試別人或被別人面試的一些題目(持續(xù)更新),答案網(wǎng)上基本都有,不一一列舉。希望能幫到需要換工作的你。 數(shù)據(jù)庫(kù) mysql 索引的理解 mysql b...
閱讀 3094·2023-04-26 00:53
閱讀 3546·2021-11-19 09:58
閱讀 1706·2021-09-29 09:35
閱讀 3311·2021-09-28 09:46
閱讀 3877·2021-09-22 15:38
閱讀 2701·2019-08-30 15:55
閱讀 3022·2019-08-23 14:10
閱讀 3839·2019-08-22 18:17