Session是怎麼實現的?存儲在哪裏?

爲什麼有session?

  首先大家知道,http協議是無狀態的,即你連續訪問某個網頁100次和訪問1次對服務器來說是沒有區別對待的,因爲它記不住你。
  那麼,在一些場合,確實需要服務器記住當前用戶怎麼辦?比如用戶登錄郵箱後,接下來要收郵件、寫郵件,總不能每次操作都讓用戶輸入用戶名和密碼吧,爲了解決這個問題,session的方案就被提了出來,事實上它並不是什麼新技術,而且也不能脫離http協議以及任何現有的web技術。
  原理很簡單,假設你訪問網頁時就像逛澡堂,第一次進去你是沒有鑰匙的,這個時候你交了錢服務檯就分配一把鑰匙給你,你走到哪裏都要帶上,因爲這是你身份的唯一標識,接下來你用這把鑰匙可以去打開一個專有的儲物櫃存儲你的衣物,游完泳,你再用鑰匙去打開櫃子拿出衣物,最後離開游泳池時,把鑰匙歸還,你的這次游泳的過程就是一次session,或者叫做會話,在這個例子中,鑰匙就是session的key,而儲物櫃可以理解爲存儲用戶會話信息的介質。
  那麼在web server中如何實現session呢?想必看了上面的例子你會很容易理解,主要是解決兩個問題,一個是鑰匙的問題,一個是存儲用戶信息的問題。對於第一個問題,即什麼東西可以讓你每次請求都會自動帶到服務器呢?如果你比較瞭解http協議,那麼答案一目瞭然,就是cookie,如果你想爲用戶建立一次會話,可以在用戶授權成功時給他一個cookie,叫做會話id,它當然是唯一的,比如PHP就會爲建立會話的用戶默認set一個名爲phpsessid,值看起來爲一個隨機字符串的cookie,如果下次發現用戶帶了這個cookie,服務器就知道,哎呀,剛剛這位顧客來了。
  剩下的是解決第二個問題,即如何存儲用戶的信息,服務器知道會話id爲abc的用戶來了,那abc想存儲自己的私人信息,比如購物車信息,如何處理?這個時候可以用內存、也可以用文件,也可以用數據庫了,但有個要求是,數據需要用用戶的會話id即可取到,比如php就默認會把會話id爲abc的用戶會話數據存儲到/tmp/phpsess_abc【1】的文件裏面,每次讀取都要反序列化程序可以理解的數據,寫的時候又需要序列化爲持久的數據格式。

如何實現session的共享?

  首先我們應該明白,爲什麼要實現共享,如果你的網站是存放在一個機器上,那麼是不存在這個問題的,因爲會話數據就在這臺機器,但是如果你使用了負載均衡把請求分發到不同的機器呢?這個時候會話id在客戶端是沒有問題的,但是如果用戶的兩次請求到了兩臺不同的機器,而它的session數據可能存在其中一臺機器,這個時候就會出現取不到session數據的情況,於是session的共享就成了一個問題。
  事實上,各種web框架早已考慮到這個問題,比如asp.NET,是支持通過配置文件修改session的存儲介質爲sql server的,所有機器的會話數據都從同一個數據庫讀,就不會存在不一致的問題;php支持把會話數據存儲到某臺memcache服務器,你也可以手工把session文件存放的目錄改爲nfs網絡文件系統,從而實現文件的跨機器共享。
  還有一個簡單的辦法可以用於會話信息不會頻繁變更的情況,在機器a設置用戶會話的時候,把會話數據post到機器b的一個cgi,機器b的cgi把會話數據存下來,這樣機器a和b都會有同一份session數據的拷貝。【2】  

SESSION 的數據保存在哪裏呢?

PHP中的session存儲

  SESSION 的數據保存在哪裏呢?
  當然是在服務器端,但不是保存在內存中,而是保存在文件或數據庫中。
   默認情況下,PHP.ini 中設置的 SESSION 保存方式是 files(session.save_handler = files),即使用讀寫文件的方式保存 SESSION 數據,而 SESSION 文件保存的目錄由 session.save_path 指定,文件名以 sess_ 爲前綴,後跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的數據即是序列化之後的 SESSION 數據了。
   如果訪問量大,可能產生的 SESSION 文件會比較多,這時可以設置分級目錄進行 SESSION 文件的保存,效率會提高很多,設置方法爲:session.save_path="N;/save_path",N 爲分級的級數,save_path 爲開始目錄。
   當寫入 SESSION 數據的時候,php 會獲取到客戶端的 SESSION_ID,然後根據這個 SESSION ID 到指定的 SESSION 文件保存目錄中找到相應的 SESSION 文件,不存在則創建之,最後將數據序列化之後寫入文件【3】。讀取 SESSION 數據是也是類似的操作流程,對讀出來的數據需要進行解序列化,生成相應的 SESSION 變量。

Java中的session存儲

  sessionid是一個會話的key,瀏覽器第一次訪問服務器會在服務器端生成一個session,有一個sessionid和它對應。tomcat生成的sessionid叫做jsessionid。
  session在訪問tomcat服務器HttpServletRequest的getSession(true)的時候創建,tomcat的ManagerBase類提供創建sessionid的方法:隨機數+時間+jvmid。
  存儲在服務器的內存中,tomcat的StandardManager類將session存儲在內存中,也可以持久化到file,數據庫,memcache,redis等。客戶端只保存sessionid到cookie中,而不會保存session,session銷燬只能通過invalidate或超時,關掉瀏覽器並不會關閉session。


  那麼Session在何時創建呢?當然還是在服務器端程序運行的過程中創建的,不同語言實現的應用程序有不同創建Session的方法,而在Java中是通過調用HttpServletRequest的getSession方法(使用true作爲參數)創建的。【4】在創建了Session的同時,服務器會爲該Session生成唯一的Session id,而這個Session id在隨後的請求中會被用來重新獲得已經創建的Session;在Session被創建之後,就可以調用Session相關的方法往Session中增加內容了,而這些內容只會保存在服務器中,發到客戶端的只有Session id;當客戶端再次發送請求的時候,會將這個Session id帶上,服務器接受到請求之後就會依據Session id找到相應的Session,從而再次使用之。


  創建:sessionid第一次產生是在直到某server端程序調用 HttpServletRequest.getSession(true)這樣的語句時才被創建。
  刪除:超時;程序調用HttpSession.invalidate();程序關閉。
  session存放在哪裏:服務器端的內存中。【5】不過session可以通過特殊的方式做持久化管理(memcache,redis)。
  session的id是從哪裏來的,sessionID是如何使用的:當客戶端第一次請求session對象時候,服務器會爲客戶端創建一個session,並將通過特殊算法算出一個session的ID,用來標識該session對象。
  session會因爲瀏覽器的關閉而刪除嗎?不會,session只會通過上面提到的方式去關閉。【6】

博主注

【1】這裏需要說明下。博主在Windows下wamp中查看到wamp安裝目錄下有tmp目錄,其中session數據保存的文件以”sess_”作爲前綴命名。在CentOS中nginx+php-fpm環境中,配置session的保存路徑(在php.ini中配置session.save_path的值,注意這個路徑需要Web服務器進程所屬用戶有權操作-讀寫操作)後,保存的session文件同樣以“sess_”作爲前綴命名。
【2】除了上述提及的方法外,解決session共享問題還有這樣一種方式:(實質上並不是通過session的共享來解決的)這裏以nginx爲例,將用戶請求分發到了不同機器上,那麼我們只需要固定,同一用戶請求分發到同一機器上進行處理,即這一次用戶來請求服務器了,那麼下一次它再來的時候,同樣也請求也被分發到與上一次相同的服務器。這樣就確保了同一用戶不會因爲請求分發到不同機器上而獲取不到session數據的問題了。
【3】後臺服務端可以將數據保存在session中,對於php而言,是將數據序列化後保存在文件中,內容如下:

USER|a:5:{s:7:"user_id";s:6:"512071";s:8:"username";s:18:"未命名的暱稱";s:5:"phone";s:11:"18888888888";s:9:"last_time";s:19:"2017-06-30 15:45:55";s:6:"is_vip";s:1:"0";}noLoginUser|a:2:{s:10:"session_id";s:40:"captcha_81cd8166e5a84a56605c5903466416da";s:11:"verify_code";s:4:"9626";}
  • 1

【4】從瀏覽器打開訪問了某一個網站,關閉瀏覽器。這樣的操作我們算一次“會話”。所以大部分就會認爲用戶訪問了網站就會產生session ID。實際上不然。例如:在Java中我們需要調用HttpServletRequest的getSession方法創建session。而在PHP中需要session_start()一下,服務器纔會將存有session ID的cookie回傳回去。否則不會有什麼session產生。
【5】“session存放在哪裏:服務器端的內存中。”指的是Tomcat保存session的方式。對於PHP而言是保存在文件中。上述有提及。
【6】session不會因爲瀏覽器的關閉而刪除。但是存有session ID的cookie的默認過期時間是會話級別。也就是用戶關閉了瀏覽器,那麼存儲在客戶端的session ID便會丟失,但是存儲在服務器端的session數據並不會被立即刪除。從客戶端即瀏覽器看來,好像session被刪除了一樣(因爲我們丟失了session ID,找不到原來的session數據了)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章