完全弄清楚session是什麼?

https://blog.csdn.net/xueqinna/article/details/51628984php

前言:html

今天就來完全的學一些session是個啥東西,我羅列了幾個須要知道的要點:web

1.session 是啥?面試

2.怎麼保存的?redis

3.如何運行?數據庫

4.有生命週期嗎?瀏覽器

5.關閉瀏覽器會過時嗎?安全

6.redis代替文件存儲session服務器

7.分佈式session的同步問題cookie

 

session是啥?

首先,我大體的知道,session是一次瀏覽器和服務器的交互的會話,會話是啥呢?就是我問候你好嗎?你回恩很好。就是一次會話,那麼對話完成後,此次會話就結束了,還有我也知道,咱們能夠將一個變量存入所有的$_SESSION['name']中,這樣php的各個頁面和邏輯都能訪問到,因此很輕鬆的用來判斷是否登錄。

這是我以前理解的session,固然也是對的,只是解釋的太膚淺,理解的太表面了,面試官若是聽到這樣的答案實際上是不太滿意的。我參考了其餘的不少資料,完全理解清楚session。

在說session是啥以前,咱們先來講說爲何會出現session會話,它出現的機理是什麼?咱們知道,咱們用瀏覽器打開一個網頁,用到的是HTTP協議,學過計算機的應該都知道這個協議,它是無狀態的,什麼是無狀態呢?就是說這一次請求和上一次請求是沒有任何關係的,互不認識的,沒有關聯的。可是這種無狀態的的好處是快速。

因此就會帶來一個問題就是,我但願幾個請求的頁面要有關聯,好比:我在www.a.com/login.php裏面登錄了,我在www.a.com/index.php 也但願是登錄狀態,可是,這是2個不一樣的頁面,也就是2個不一樣的HTTP請求,這2個HTTP請求是無狀態的,也就是無關聯的,因此沒法單純的在index.php中讀取到它在login.php中已經登錄了!

那咋搞呢?我不可能這2個頁面我都去登錄一遍吧。或者用笨方法這2個頁面都去查詢數據庫,若是有登錄狀態,就判斷是登錄的了。這種查詢數據庫的方案雖然可行,可是每次都要去查詢數據庫不是個事,會形成數據庫的壓力。

因此正是這種訴求,這個時候,一個新的客戶端存儲數據方式出現了:cookie。cookie是把少許的信息存儲在用戶本身的電腦上,它在一個域名下是一個全局的,只要設置它的存儲路徑在域名www.a.com下 ,那麼當用戶用瀏覽器訪問時,php就能夠從這個域名的任意頁面讀取cookie中的信息。因此就很好的解決了我在www.a.com/login.php頁面登錄了,我也能夠在www.a.com/index.php獲取到這個登錄信息了。同時又不用反覆去查詢數據庫。

雖然這種方案很不錯,也很快速方便,可是因爲cookie 是存在用戶端,並且它自己存儲的尺寸大小也有限,最關鍵是用戶能夠是可見的,並能夠隨意的修改,很不安全。那如何又要安全,又能夠方便的全局讀取信息呢?因而,這個時候,一種新的存儲會話機制:session 誕生了。

我擦,終於把session是怎麼誕生的給圓清楚了,不容易啊!!!

好,session 誕生了,從上面的描述來說,它就是在一次會話中解決2次HTTP的請求的關聯,讓它們產生聯繫,讓2兩個頁面都能讀取到找個這個全局的session信息。session信息存在於服務器端,因此也就很好的解決了安全問題。

session的運行機制和是怎麼保存的?

既然,它也是一種服務區存儲數據的方式,確定也是存在服務器的某個地方了。確實,它存在服務器的/tmp 目錄下,這一點咱們接下來慢慢講。

咱們先說下它的運行機制,是怎麼分配的。咱們主要用PHP中session的機制,其實各類語言都差很少。

若是這個時候,咱們須要用到session,那咱們第一步怎麼辦呢?第一步是開啓session:

session_start();

這是個無任何返回值的函數,既不會報錯,也不會成功。它的做用是開啓session,並隨機生成一個惟一的32位的session_id,相似於這樣:

4c83638b3b0dbf65583181c2f89168ec

session的所有機制也是基於這個session_id,它用來區分哪幾回請求是一我的發出的。爲何要這樣呢?由於HTTP是無狀態無關聯的,一個頁面可能會被成百上千人訪問,並且每一個人的用戶名是不同的,那麼服務器如何區分此次是小王訪問的,那次是小名訪問的呢?因此就有了找個惟一的session_id 來綁定一個用戶。一個用戶在一次會話上就是一個session_id,這樣成千上萬的人訪問,服務器也能區分究竟是誰在訪問了。

咱們作個試驗,看看,是否是這樣的:

咱們在php.iyangyi.com 域名下的a.php 頁面中,輸入以下代碼:

session_start(); echo "SID: ".SID."<br>"; echo "session_id(): ".session_id()."<br>"; echo "COOKIE: ".$_COOKIE["PHPSESSID"];

咱們訪問一下a.php頁面,看能輸出什麼?

咱們看到竟然還有一個警告。咱們先一個一個的看。首先SID這個常量,咱們沒有給它賦值,它竟然能有輸出,其次session_id()這個系統方法是輸出本次生成的session_id。最後$_COOKIE['PHPSESSIID'] 沒有值,這個咱們接下來講。

好,咱們再次刷新這個頁面,咱們能看到什麼?

奇怪的事情發生了。SID 沒有值了,$_COOKIE['PHPSESSID']中有值了。並且,2次刷新,session_id 都是同樣

的:bjvlo4p38cfqkr1hr7pe924ts3,實際狀況下,只要不關閉網頁,怎麼刷新都是同樣:

既然咱們看到COOKIE中有值了,咱們,打開firebug開看究竟是什麼:

並且這個PHPSESSID的過時時間是會話,什麼意思呢?就是瀏覽器只要不關就一直不存,瀏覽器一關就過時,消失了。

好,咱們關掉瀏覽器,從新打開a.php頁面,看看有沒有什麼變化:

你看,是否是又回到當初第一次打開時候的樣子。

OK,解惑的時候到了:

每次咱們訪問一個頁面,若是有開啓session,也就是有session_start() 時,就會自動生成一個session_id 來標註是此次會話的惟一ID,同時也會自動往cookie裏寫入一個名字爲PHPSESSID的變量,它的值正是session_id,當此次會話沒結束,再次訪問的時候,服務器會去讀取這個PHPSESSID的cookie是否有值有沒過時,若是可以讀取到,則繼續用這個session_id,若是沒有,就會新生成一個session_id,同時生成PHPSESSID這個cookie。因爲默認生成的這個PHPSESSID cookie是會話,也就是說關閉瀏覽器就會過時掉,因此,下次從新瀏覽時,會從新生成一個session_id。

好,這個是session_id,就用來標識綁定一個用戶的,既然session_id生成了。那麼當咱們往session裏面寫入數據,是如何保存的,答案是保存在服務器的臨時目錄裏,根據php.ini的配置,我機子上的這個session是存在D:\wamp\tmp 目錄裏的。咱們先說是存在這個目錄下,而後待會將如何修改。

那麼它是怎麼存的呢?

一樣也是用到session_id。session_id是32位的,服務器會用 sess_前綴 + session_id 的形式存在這個臨時目錄下,好比上面這個例子:

 

因此,每一次生成的session_id都會生成一個這樣的文件,用來保存此次會話的session信息。

咱們往session裏寫入些數據,來看看session是怎麼往這個文件裏寫數據的,咱們一樣在a.php頁面繼續加上寫入session的語句:

 

$_SESSION['hello'] = 123;

$_SESSION['word'] = 456;

而後,我刷新頁面,因爲我並無關閉頁面,就這是說此次會話還沒結束,那麼確定還會是一樣的session_id : bjvlo4p38cfqkr1hr7pe924ts3

而後,咱們 用編輯器打開它的存儲文件sess_bgg20mcl86drbt3j08jg5h5h17這個文件,看看裏面是啥?

hello|i:123;word|i:456;

是序列化的數據,咱們肉眼也能讀出來。當咱們往$_SESSION全局變量裏寫數據時,它會自動往這個文件裏寫入。讀取session的時候,也會根據session_id 找到這個文件,而後讀取須要的session變量。

這個sess文件不會隨着客戶端的PHPSESSID過時,也一塊兒過時掉,它會一直存在,出息GC掃描到它過時或者使用session_destroy()函數摧毀,咱們在下面講到session·回收的時候會說到。

咱們大體總結下:

HTTP請求一個頁面後,若是用到開啓session,會去讀cookie中的PHPSESSID是否有,若是沒有,則會新生成一個session_id,先存入cookie中的PHPSESSID中,再生成一個sess_前綴文件。當有寫入$_SESSION的時候,就會往sess_文件裏序列化寫入數據。當讀取的session變量的時候,先會讀取cookie中的PHPSESSID,得到session_id,而後再去找這個sess_sessionid文件,來獲取對應的數據。因爲默認的PHPSESSID是臨時的會話,在瀏覽器關閉後,會消失,因此,咱們從新訪問的時候,會新生成session_id和sess_這個文件。

好。session生成和保存將清楚了。咱們再來看前面提到的幾個變量:

echo "SID: ".SID."<br>";

echo "session_id(): ".session_id()."<br>";

echo "COOKIE: ".$_COOKIE["PHPSESSID"];

SID 是一個系統常量,SID包含着會話名以及會話 ID 的常量,格式爲 "name=ID",或者若是會話 ID 已經在適cookie 中設定時則爲空字符串,第一次顯示的時候輸出的是SID的值,當你刷新的時候,由於已經在cookie中存在,因此顯示的是一個空字符串。

session_id() 函數用來返回當前會話的session_id,它會去讀取cookie中的name,也就是PHPSESSID值。

session的相關配置

上面巴拉巴拉廢話說了那麼多,應該是能夠理解session的一套機制了的,我接下來看看,前面零星的提到了php.ini裏面有關於session相關的配置。咱們打開php.ini來,搜索session相關,我主要把用到的幾個給列出來:

[Session]

session.save_handler = files

session.save_path = "d:/wamp/tmp"

session.use_cookies = 1

session.name = PHPSESSID

session.auto_start = 0

session.cookie_lifetime = 0

session.serialize_handler = php

session.gc_divisor = 1000

session.gc_probability = 1

session.gc_maxlifetime = 1440

主要咱們用到的,常見的大概就是這幾個。咱們一個一個的說。

session.save_handler = files 表示的是session的存儲方式,默認的是files文件的方式保存,sess_efdsw34534efsdfsfsf3r3wrresa, 保存在 session.save_path = "d:/wamp/tmp" 裏,全部這2個都是可配值得。咱們上面的例子就是用的這種默認的方式。

save_handler 不只僅只能用文件files,還能夠用咱們常見的memcache 和 redis 來保存。這個咱們後面來講。

session.use_cookies 默認是1,表示會在瀏覽器裏建立值爲PHPSESSID的session_id,session.name = PHPSESSID 找個配置就是改這個名字的,你能夠改爲PHPSB, 那這樣就再瀏覽器裏生成名字爲PHPSB的session_id 。`(*∩_∩*)′

session.auto_start = 0 用來是否須要自動開啓session,默認是不開啓的,全部咱們須要在代碼中用到session_start();函數開啓,若是設置成1,那麼session_id 也會自動就生成了。

session.cookie_lifetime = 0 這個是設置在客戶端生成PHPSESSID這個cookie的過時時間,默認是0,也就是關閉瀏覽器就過時,下次訪問,會再次生成一個session_id。因此,若是想關閉瀏覽器會話後,但願session信息可以保持的時間長一點,能夠把這個值設置大一點,單位是秒。

gc_divisor, gc_probability, gc_maxlifetime 這3個也是配合一塊兒使用,他們是幹嗎的呢?他們是幹大事情的,回收這些sess_xxxxx 的文件,它是按照這3個參數,組成的比率,來啓動GC刪除這些過時的sess文件。gc_maxlifetime是sess_xxx文件的過時時間。具體能夠參考這個,我以爲他說我比我清楚: session的垃圾回收機制

session的垃圾回收

咱們經過上面的各類,已經清楚session的種種了,它會產生各類的sess_前綴的文件,長此以往就會造成垃圾數據,並且正常的session讀取也會形成壓力,因此及時的清理是蠻有用的。

1. 代碼處理

php代碼中有幾個函數是用來清理過時的session信息的,主要是這幾個:

unset($_SESSION['hello']); session_unset(); session_destroy();

setcookie(session_name(), '', time()-42000, '/');

unset 這是是經常使用的銷燬標量的方法,很少說,惟一要說的是刪除session ,就是將這個sess_xxx的文件的hello變量給刪除了,其餘的變量該有的都保存着。而 session_unset() 這個不穿參數,這個是銷燬sess_xxx文件中的全部變量,可是這個sess_xxx文件仍是保存着。而session_destroy 則更狠角了,它是直接將這個sess_xxx文件給刪掉。

通常退出操做裏面,咱們也會將session_name() 得到到的PHPSESSID也給過時掉,刪掉,由於網頁沒關,不這樣刪除的話,刷新以後,找個值是存在的,服務器將會從新建立一個如出一轍session_id的sess文件。

2. php gc 自動刪除

php.ini中的幾個銷燬sess_xxx文件的配置,在上面說了:

session.gc_divisor = 1000
session.gc_probability = 1
session.gc_maxlifetime = 1440

簡單說下,其實上面的一個超連接的博客講的很清楚了,php觸發gc刪除過時的sess_x的文件的概念是這樣計算的:機率= gc_probability/gc_divisor,上面的默認的參數,也就是說概念是1/1000的概念,在頁面啓動session_start() 函數時候,會觸發gc刪除過時的sess_文件。這個機率實際上是蠻小的

因此,咱們能夠將這個概念調整大一點,好比:將gc_probability 也調成1000,那gc_probability/gc_divisor 就等於1了,也就是百分一百會觸發。這樣就垃圾回收機率就大的多。

用redis存儲session

上面七七八八說了不少關於session的存儲啊機制啊等。如今說說若是用redis 存儲session。以前說的都是用文件files存儲,如今想用redis,好處有哪些?

  1. 更快的讀取和寫入速度。redis是直接操縱內存數據的,確定是要比文件的形式快不少。

  2. 更好的設置好過時時間。文件存儲的sess_sdewfrsf文件其實被刪除掉仍是要考運氣的和機率的,頗有可能形成sess_文件沒即時刪除,形成存儲磁盤空間過多,和讀取SESSION就變慢了。

  3. 更好的分佈式同步。設置redis 的主從同步,能夠快速的同步session到各臺web服務器,比文件存儲更加快速。

總的說來,用redis來存儲SESSION速度更快,性能更高。

 

要作的第一件事,固然就是安裝redis了。具體安裝和配置php與redis,就不細說了,能夠參考我寫的redis相關:redis安裝與配置

redis 安裝好了以後,接下來就是修改php.ini了。將原來的files 改爲redis:

session.save_handler = redis

session.save_path = "tcp://127.0.0.1:6381"

須要用到tcp來鏈接redis,若是你設置reids 有密碼訪問的話,這樣加上就能夠了:tcp://127.0.0.1:6381?auth=authpwd

重啓web服務器後,你就能夠正常使用SESSION了。和以前使用files存儲SESSION如出一轍。

咱們看下redis 是怎麼存儲session的。它是用了有別於文件存儲使用sess_前綴的名字,它用PHPREDIS_SESSION: 前綴,再加上session_id 的形式,是一個string 字符串類型,帶生存週期的。

PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5

你會發現,它的值和文件存儲session如出一轍,都是用php序列化後存儲,並且有明確的過時時間,是根據配置:session.gc_maxlifetime = 1440 來設定的,默認1440秒。固然你能夠修改爲其餘的。

咱們寫入和讀取每頁仍是如出一轍,包括刪除和狀況,都是如出一轍,沒有什麼變化:

session_start(); //開啓session,若是讀不到cookie,會從新生成一個session_id,redis裏面也會新生成一個。

echo "SID: ".SID."<br>";

echo "session_id(): ".session_id()."<br>";

echo "COOKIE: ".$_COOKIE["PHPSESSID"];

$_SESSION['hello'] = 123; // 寫入session 。會序列化後寫入redis中
$_SESSION['word'] = 456;

var_dump($_SESSION['word']);  //讀session。會從redis讀到,解序列後,讀出這個值。redis 1440秒過時後,將讀不到。
unset($_SESSION['hello']);  // 刪除 hello 的session 。會刪除 redis的hello值
session_unset();  // 清空redis 中這個session_id的全部值。
session_destroy(); // 刪除掉這個PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5 key。

session同步

在作了web集羣后,你確定會首先考慮session同步問題,由於經過負載均衡後,同一個IP訪問同一個頁面會被分配到不一樣的服務器上,若是不一樣的服務器用的是不一樣的reidis服務,那麼可能就會出現,一個登陸用戶,一會是登陸狀態,一會又不是登陸狀態。因此session這個時候就要同步了。恰好,咱們選擇用redis做爲了存儲,是能夠在多臺redis 服務器中同步的。

具體能夠搜索 reidis主從同步或者redis 集羣

參考資料:http://zhidao.baidu.com/link?url=2_phukSt0xI6SSIVKUE37TxzivLqdCz_JCPhIUPLMB3TX_IWgoVKL2lwDn1Gh7xTykyV3ezU1YQv9s6HD3uhO_

http://blog.sina.com.cn/s/blog_5f54f0be0100xs7e.html

http://star0708.blog.163.com/blog/static/181091248201341710100381/

http://baike.baidu.com/view/25258.htm?fr=aladdin

http://www.cnblogs.com/hongfei/archive/2012/06/17/2552434.html

相關文章
相關標籤/搜索