摘要:在中,會為每個不同的用戶生成一個隨機的,每個人擁有的都是不同的。值得注意的是,過期了不代表這個文件會馬上被垃圾回收機制刪除掉,還是有可能會殘存一段時間的。
原文地址:https://t.ti-node.com/thread/...
這基本上算是個老舊的話題了,幾乎所有phper在第一次面試的時候都會被問到關于session的問題,如果不出意外,往往是如下三板斧:
php的session是什么東西
php的session存在什么地方、時候過期
php的session和cookie有什么區別
這三個問題堪稱是關于php session三大基礎問題了,要是掌握不好,直接導致面試掛掉,令人唏噓不已。
就以上三個問題簡單回顧一下:
session翻譯成中文,大抵就是會話。我們知道http協議是一個無狀態的協議,用極為粗暴的土話表示無狀態就是說“你壓根不知道這個http請求是哪個犢子發起的”或者“你永遠不知道這個http請求的那頭到底是上次那個哈士奇還是上上次那個胖子”,所以為了解決這個問題,php引入session來額外標記“到底誰發起的這個http請求”。在php中,php會為每個不同的用戶生成一個隨機的session id,每個人擁有的session id都是不同的。用戶在與服務器產生的每一次交互中,都是利用session id來辨別的用戶。讓php產生session是一件很容易的事情,直接調用session_start()函數就可以了,如下圖就是產生的session文件:
其中sess是文件前綴,后面的“njjf8l3lh**ff6”就是你的session id了。但是現在session文件內容是空的,如果我們用如下代碼,就可以產生session文件的內容:
刷新一下網頁,然后在再次查看原來的session文件,其中就會有如下內容:
就是說,你往全局變量$_SESSION保存的內容本質上都是PHP用文本形式給你存儲到服務器上了。服務器根據你的session id讀取相應的session文件然后把其中內容讀出來,你就會得到你的$_SESSION數據。php的session默認是以文件的形式存儲的系統磁盤中,在運行于ubuntu 16.04系統的php 7.0.28中,session是存儲于/var/lib/php/sessions文件夾下,可以通過php.ini配置文件中的session.save_path來查看確定。php的過期時間是由php配置項session.gc_maxlifetime來確定的,值的單位是秒鐘,默認是1440,也就是說當這個session文件具體上次修改時間超過了1440秒后這個session文件就算是過期了。值得注意的是,過期了不代表這個session文件會馬上被垃圾回收機制刪除掉,還是有可能會殘存一段時間的。那么,究竟何時會被刪除?這取決于session.gc_probability和session.gc_divisor兩個配置項了。這兩個選項的比值 ( session.gc_probability / session.gc_divisor ) 就是觸發垃圾回收機制的概率,比如 ( 1 / 100 ) 就可以簡單粗暴的理解為“每產生100個請求,就有1次會觸發php垃圾回收機制去刪除過期的session文件”,所以你記住了:在php中如果你想要一個精確過期的session文件,最起碼默認的session配置是絕對不可能的。話說回來,還不都是因為php并沒有啟動一個多帶帶的線程或者進程去掃描垃圾,所以,也只能用這個“概率”這種粗暴的方式來解決這個問題,又不是不能用。
開篇說了,為了搞明白“到底是哈士奇還是胖子”的問題,不得不引入額外標記數據才行,所以實際上,先有的cookie而有后的session,都是為了解決這個問題而產生的。二者的恩怨情仇在于:
cookie存在于客戶端,而session存在于服務器端,所以session相對更安全
服務器可以讀取session和cookie,但是客戶端(也就是瀏覽器)只能讀取cookie
默認情況下,session是離不開cookie的,說到底“安全的session使用方式”必須依靠cookie才能混口飯吃。因為session id就是依靠cookie保存起來的,客戶端瀏覽器每次發送請求都會攜帶上該cookie,該cookie默認名稱是PHPSESSID,數值就是session id。
如果說就是用不了cookie,那么session也并不是真的不能用。禁用cookie的情況下,session可以通過配置利用URL來傳遞,也就是query string直接暴露在網址中,非常不安全非常嚇人,嚴重不推薦這種方式,甚至應該直接禁用!
cookie大小稍微有限制(據說考慮用localstorage代替?),session相對寬松
大概就這些,不再贅述,我是建議大家配合php.ini文件去研究上面三個問題。
如果說真的只回顧一下這三個問題,那豈不是真的應了“一看標題猛如虎,打開內容1-5”?我說過了的,我這里是個正經的博客網站,是個真正的有些內涵的php文化網站,不能只講些個初級的內容,是個話題都都要無論如何強塞點兒看起來高端的玩意進去撐場面。
剛叨叨過了,默認配置下session是以文本文件形保存在服務器的某個文件夾中的,有心的人應該知道“一個目錄中文件過多是會降低讀取效率的”,所以,在用一些PC軟件的時候可以看到這些軟件會把TA需要的數據分散開來到不同的次級目錄中去。php的session文件也可以這么干,總體來說是比較簡單粗暴的。我們需要關注下兩個php配置項:
一個是session.save_handler,默認這貨的值是“files”,也就是文件
一個是session.save_path,默認這貨的值是一個目錄路徑,比如/var/lib/php/session。現在我們將這個值改成類似于session.save_path = "N;/path"這樣的,其中N是一個正整數,這個數值的含義就是指將目錄分成幾個層次,比如我們修改成session.save_path = “2;/var/lib/php/sessions”,然后重啟一下apache或者fpm進程管理器,然后執行如下代碼:
刷新網頁,如下圖所示:
錯誤原因相比大家看到了,大概意思就是說“/var/lib/php/sessions/n/j/”這個文件夾不存在,那么切換到這個目錄下看看,如下圖:
果然是空的,也就說沒有/n/j這個子目錄,看來得手工創建了。然而,真的不能去手工創建,因為你哪兒知道文件夾的名字是啥?回到配置文件一頓研究,在session.save_path配置項附近發現如下英文字樣:; NOTE 1: PHP will not create this directory structure automatically. ; You can use the script in the ext/session dir for that purpose. ; NOTE 2: See the section on garbage collection below if you choose to ; use subdirectories for session storage英文比較蹩腳昂,大概翻譯一下,多包涵:
; NOTE 1: PHP壓根不會幫你創建這些文件夾,您自己個兒下載php源碼包,到ext目錄的session目中去找那個腳本去創建 ; NOTE 2: 如果你要用子目錄存儲session的話,記得看下垃圾回收,不看就有坑。(坑在這里直接告訴大家吧,大概就是說你要自己搞子目錄存session,那我那個靠信仰和概率才能觸發的垃圾回收機制就壓根就不觸發了,你自己想辦法搞定你的過期session,我不管了)所以呢,我們下載一個php源碼包,最好是和你運行環境版本一樣的php源碼包并解壓,命令行切到ext/session目錄下,如下圖:
看到那個mod_files.sh沒?Linux下就這腳本。mod_files.bat就是給windows用的。給這個腳本chmod +x mod_files.sh加個執行權限,然后查看下使用方式:
為了幫助眼近視的讀者,友情翻譯一下使用方式:./mod_files.sh "session文件根目錄" 目錄深度 哈希函數比特量 對應我的php開發環境就是: ./mod_files.sh /var/lib/php/sessions/ 2 5 其中第一項就是你存儲session的根目錄,第二項就是那個N,第三項查看session.hash_bits_per_character配置項然后執行,如下圖所示:
此時到/var/lib/php/sessions中查看下,果然有目錄了,那么,再次刷新網頁,本以為很順利的你可能依然會遇到錯誤,如下:session_start(): open(/var/lib/php/sessions/n/j/sess_njjf8l3lhfrpq8nrlnl1d9qff6, O_RDWR) failed: Permission denied (13)模模糊糊認得Permission denied這幾個字母,好像是權限的問題,難道是因為當前apache進程用戶或者fpm進程用戶沒有權限往這些目錄寫數據嗎?改下這些目錄的擁有者撒,改成www-data(我系統中fpm的運行用戶),再試試,果然好了!
總有刁民以為這就可以解決很大的問題了,然而很悲劇的是:并不是。當前這個方案一定程度可以解決session文件過多的問題,但是依然有兩個問題沒有得到解決:
依然是文件存儲,如果訪問量太大的話,session文件從硬盤的讀取IO或許會成為程序的瓶頸,當然SSD速度一定會好很多
如果網站分別部署到了兩臺服務器上,session無法共享,出現故障。什么意思呢?就是為了保證高可用,網站程序分別在A服務器上和B服務器上,然后最外面使用一臺nginx擋在最前面做負載均衡,路人甲的某次http請求可能會被分配到A上,也可能會被分配到B上。路人甲在A上產生的session文件會被保存到A服務器硬盤上,但是服務器B上卻沒有,如果該用戶請求被打到B上的時候,很不幸,session丟失了,一些數據也就會丟失,路人甲八成會罵娘罵客服。也就說,A服務器和B服務器需要共享同一套session!
借此,就引入一個問題,就是分布式web部署中,如何解決session共享的問題!
關子我就不賣了,沒意思,首先想到的是redis,為A和B提供一臺C redis服務器就可以了,這樣可以“ 多快好省 ”地一舉解決問題!按照預想,引入redis后可以順利解決三個問題:
內存級的讀寫速度,唰唰唰!
session輕松easy實現了共享,哪怕以后業務服務器繼續橫向擴展到服務器D
session的過期終于可以精確到秒了,說沒就沒,不用再靠信仰和概率了
將session存入redis需要修正如下兩處php配置,首先設置session.save_handler = redis,其次是設置ession.save_path = "tcp://127.0.0.1:6379",然后重啟apache或者fpm,刷新一下網頁,如果網頁不報什么錯誤,理論上session數據就已經到redis中去了,連接redis查看下key,如下圖:
從上至下我一共執行了五次redis命令,分別表示:
查看所有keys從而獲取php分配給我的session文件名稱
獲取這個key的剩余時間,過期后redis該key算失效了
查看這個key的數據類型,可以看出是string類型
使用get直接獲取string的值
過了一段時間,我又刷新了一下網頁,然后再次用ttl看該key的剩余時間,再次被延長到1440秒了
除了redis外(memcache我就不舉例了,和redis類似),還有一種方案就是通過nfs共享來實現,大概原理就是弄一臺服務器,通過內網共享對所有php業務服務器開放讀寫,大家知道linux下磁盤是可以掛載在某個文件夾下的,所以將nfs掛載到各個php業務服務器的某個目錄下,然后按照上述文章修改響應配置就可以了。這個,我也沒有嘗試過也懶得自己去嘗試了,所以偷個懶直接給大家拋個連接吧,是老葉博客上的一篇文章,《iMySQL | 老葉茶館,PHP實現多服務器session共享之NFS共享》。
裝逼完畢,如有問題,火速留言指正!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29749.html
摘要:在給老師做項目的這個期間,我也發現慕課網,然后便貪婪的在上面學習,其實現在看看上面的教學也是一些基礎,想要真的學到很多,必須做實際項目,遇到問題,然后去百度或者谷歌這個問題,看別人的博客中怎么教你解決。 前言:想當初我也是個無知騷年,學完學校的C語言基礎課程之后也就再也沒有什么想法了(期間還看了看JAVA,但是發現并不能夠學好,索性就放棄了),由于當時期末C語言程序答辯的時候,我自己仿...
摘要:在給老師做項目的這個期間,我也發現慕課網,然后便貪婪的在上面學習,其實現在看看上面的教學也是一些基礎,想要真的學到很多,必須做實際項目,遇到問題,然后去百度或者谷歌這個問題,看別人的博客中怎么教你解決。 前言:想當初我也是個無知騷年,學完學校的C語言基礎課程之后也就再也沒有什么想法了(期間還看了看JAVA,但是發現并不能夠學好,索性就放棄了),由于當時期末C語言程序答辯的時候,我自己仿...
摘要:在給老師做項目的這個期間,我也發現慕課網,然后便貪婪的在上面學習,其實現在看看上面的教學也是一些基礎,想要真的學到很多,必須做實際項目,遇到問題,然后去百度或者谷歌這個問題,看別人的博客中怎么教你解決。 前言:想當初我也是個無知騷年,學完學校的C語言基礎課程之后也就再也沒有什么想法了(期間還看了看JAVA,但是發現并不能夠學好,索性就放棄了),由于當時期末C語言程序答辯的時候,我自己仿...
閱讀 787·2021-11-11 16:54
閱讀 1517·2021-08-24 10:01
閱讀 1911·2019-08-30 15:54
閱讀 3296·2019-08-29 14:02
閱讀 3130·2019-08-28 18:22
閱讀 2245·2019-08-28 18:09
閱讀 3698·2019-08-26 10:26
閱讀 2665·2019-08-23 18:23