Session其實分為客戶端Session和服務器端Session。
當用戶首次與Web服務器建立連接的時候,服務器會給用戶分發一個 SessionID作為標識。SessionID是一個由24個字符組成的隨機字符串。用戶每次提交頁面,瀏覽器都會把這個SessionID包含在 HTTP頭中提交給Web服務器,這樣Web服務器就能區分當前請求頁面的是哪一個客戶端。這個SessionID就是保存在客戶端的,屬於客戶端Session。
其實客戶端Session默認是以cookie的形式來存儲的,所以當用戶禁用了cookie的話,服務器端就得不到SessionID。這時我們可以使用url的方式來存儲客戶端Session。也就是將SessionID直接寫在了url中,當然這種方法不常用。
sessionid如何產生?由誰產生?保存在哪里?
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會因為瀏覽器的關閉而刪除嗎?
Cookie分為內存中Cookie(也可以說是進程中Cookie)和硬盤中Cookie。大部分的Session機制都使用進程中Cookie來保存Session id的,關閉瀏覽器后這個進程也就自動消失了,進程中的Cookie自然就消失了,那么Session id也跟着消失了,再次連接到服務器時也就無法找到原來的Session了。
當然,我們可以在登陸時點擊下次自動登錄,比如說CSDN的“記住我一周”,或者我們的購物車信息可以在切換不同瀏覽器時依然可用。這就要用到我們上文提到的另一種Cookie了——硬盤中Cookie,這時Session id將長期保存在硬盤上的Cookie中,直到失效為止。
tomcat中session的創建:
ManagerBase是所有session管理工具類的基類,它是一個抽象類,所有具體實現session管理功能的類都要繼承這個類,該類有一個受保護的方法,該方法就是創建sessionId值的方法:
(tomcat的session的id值生成的機制是一個隨機數加時間加上jvm的id值,jvm的id值會根據服務器的硬件信息計算得來,因此不同jvm的id值都是唯一的),
StandardManager類是tomcat容器里默認的session管理實現類,
它會將session的信息存儲到web容器所在服務器的內存里。
PersistentManagerBase也是繼承ManagerBase類,它是所有持久化存儲session信息的基類,PersistentManager繼承了PersistentManagerBase,但是這個類只是多了一個靜態變量和一個getName方法,目前看來意義不大,對於持久化存儲session,tomcat還提供了StoreBase的抽象類,它是所有持久化存儲session的基類,另外tomcat還給出了文件存儲FileStore和數據存儲JDBCStore兩個實現。
session是解決http協議無狀態問題的服務端解決方案,它能讓客戶端和服務端一系列交互動作變成一個完整的事務,能使網站變成一個真正意義上的軟件
擴展:
會話cookie和持久cookie的區別
如果不設置過期時間,則表示這個cookie生命周期為瀏覽器會話期間,只要關閉瀏覽器窗口,cookie就消失了。這種生命期為瀏覽會話期的cookie被稱為會話cookie。會話cookie一般不保存在硬盤上而是保存在內存里。
如果設置了過期時間,瀏覽器就會把cookie保存到硬盤上,關閉后再次打開瀏覽器,這些cookie依然有效直到超過設定的過期時間。
存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享,比如兩個IE窗口。而對於保存在內存的cookie,不同的瀏覽器有不同的處理方式。
保存session id的幾種方式
A.保存session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規則把這個標識發送給服務器。
B.由於cookie可以被人為的禁止,必須有其它的機制以便在cookie被禁止時仍然能夠把session id傳遞回服務器,經常采用的一種技術叫做URL重寫,就是把session id附加在URL路徑的后面,附加的方式也有兩種,一種是作為URL路徑的附加信息,另一種是作為查詢字符串附加在URL后面。網絡在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑后面都包含這個session id。
C.另一種技術叫做表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞回服務器。
session什么時候被創建
一個常見的錯誤是以為session在有客戶端訪問時就被創建,然而事實是直到某server端程序(如Servlet)調用HttpServletRequest.getSession(true)這樣的語句時才會被創建。
session何時被刪除
session在下列情況下被刪除:
A.程序調用HttpSession.invalidate()
B.距離上一次收到客戶端發送的session id時間間隔超過了session的最大有效時間
C.服務器進程被停止
再次注意關閉瀏覽器只會使存儲在客戶端瀏覽器內存中的session cookie失效,不會使服務器端的session對象失效。
getSession()/getSession(true)、getSession(false)的區別
getSession()/getSession(true):當session存在時返回該session,否則新建一個session並返回該對象
getSession(false):當session存在時返回該session,否則不會新建session,返回null
使用isNew來判斷用戶是否為新舊用戶的錯誤做法
public boolean isNew()方法如果會話尚未和客戶程序(瀏覽器)發生任何聯系,則這個方法返回true,這一般是因為會話是新建的,不是由輸入的客戶請求所引起的。
但如果isNew返回false,只不過是說明他之前曾經訪問該Web應用,並不代表他們曾訪問過我們的servlet或JSP頁面。
因為session是與用戶相關的,在用戶之前訪問的每一個頁面都有可能創建了會話。因此isNew為false只能說用戶之前訪問過該Web應用,session可以是當前頁面創建,也可能是由用戶之前訪問過的頁面創建的。
正確的做法是判斷某個session中是否存在某個特定的key且其value是否正確
session cookie和session對象的生命周期是一樣的嗎
當用戶關閉了瀏覽器雖然session cookie已經消失,但session對象仍然保存在服務器端
是否只要關閉瀏覽器,session就消失了
程序一般都是在用戶做log off的時候發個指令去刪除session,然而瀏覽器從來不會主動在關閉之前通知服務器它將要被關閉,因此服務器根本不會有機會知道瀏覽器已經關閉。服務器會一直保留這個會話對象直到它處於非活動狀態超過設定的間隔為止。
之所以會有這種錯誤的認識,是因為大部分session機制都使用會話cookie來保存session id,而關閉瀏覽器后這個session id就消失了,再次連接到服務器時也就無法找到原來的session。
如果服務器設置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發出的HTTP請求報頭,把原來的session id發送到服務器,則再次打開瀏覽器仍然能夠找到原來的session。
恰恰是由於關閉瀏覽器不會導致session被刪除,迫使服務器為session設置了一個失效時間,當距離客戶上一次使用session的時間超過了這個失效時間時,服務器就可以認為客戶端已經停止了活動,才會把session刪除以節省存儲空間。
由此我們可以得出如下結論:
關閉瀏覽器,只會是瀏覽器端內存里的session cookie消失,但不會使保存在服務器端的session對象消失,同樣也不會使已經保存到硬盤上的持久化cookie消失。
session共享問題
當下的互聯網網站為了提高網站安全性和並發量,服務端的部署的服務器的數量往往是大於或等於兩台,多台服務器對外提供的服務是等價的,但是不同的服務器上面肯定會有不同的web容器,由上面的講述我們知道session的實現機制都是web容器里內部機制,這就導致一個web容器里所生成的session的id值是不同的,因此當一個請求到了A服務器,瀏覽器得到響應后,客戶端存下的是A服務器上所生成的session的id,當在另一個請求分發到了B服務器,B服務器上的web容器是不能識別這個session的id值,更不會有這個sessionID所對應記錄下來的信息,這個時候就需要兩個不同web容器之間進行session的同步。
一般大型互聯公司的網站都是有一個個獨立的頻道所組成的,例如我們常用的百度,會有百度搜索,百度音樂,百度百科等等,我相信他們不會把這些不同頻道都給一個開發團隊完成,應該每個頻道都是一個獨立開發團隊,因為每個頻道的應用的都是獨立的web應用,那么就存在一個跨站點的session同步的問題,跨站點的登錄可以使用單點登錄的(SSO)的解決方案,但是不管什么解決方案,跨站點的session共享任然是逃避不了的問題。
解決session相關問題的技術方案
由上所述,session一共有兩個問題需要解決:
1) session的存儲應該獨立於web容器,也要獨立於部署web容器的服務器;
2)如何進行高效的session同步。
在講到解決這些問題之前,我們首先要考慮下session如何存儲才是高效,是存在內存、文件還是數據庫了?文件和數據庫的存儲方式都是將session的數據固化到硬盤上,操作硬盤的方式就是IO,IO操作的效率是遠遠低於操作內存的數據,因此文件和數據庫存儲方式是不可取的,所以將session數據存儲到內存是最佳的選擇。因此最好的解決方案就是使用分布式緩存技術,例如:memcached和redis,將session信息的存儲獨立出來也是解決session同步問題的方法。