一、寫在前面 我們在使用ASP.NET開發(fā)的過程中,有時(shí)會進(jìn)行數(shù)據(jù)存儲以實(shí)現(xiàn)請求前后的狀態(tài)保持(HTTP是無狀態(tài)保持的協(xié)議),而Session作為一種快速簡單易于實(shí)現(xiàn)的方式被我們經(jīng)常使用,當(dāng)然如果出于性能方面的考量,我們還是不建議往Seesion中塞入更多的東西,最好是不用Session。 還有一點(diǎn)需要說明的是,Session實(shí)現(xiàn)的本質(zhì)是在客戶端產(chǎn)生一個(gè)SessionId,具體的數(shù)據(jù)存儲在服務(wù)器端,客戶端通過SessionId來獲取服務(wù)器端的具體數(shù)據(jù),那這個(gè)SeesionId是怎么保存在客戶端以及又是以什么方式來傳給服務(wù)器的呢?服務(wù)器端又是以什么方式保存Session的這些數(shù)據(jù)的呢?各種方式的優(yōu)缺點(diǎn)又是什么?這就是本篇隨筆想要記錄的內(nèi)容。 二、配置方式 <sessionState mode='Off|InProc|StateServer|SQLServer' cookieless='true|false' timeout='number of minutes' stateConnectionString='tcpip=server:port' sqlConnectionString='sql connection string' stateNetworkTimeout='number of seconds' /> 上面是Session在Web.config的配置方式,下面對各個(gè)節(jié)點(diǎn)做一些簡單的介紹 mode(設(shè)置將服務(wù)器的Session信息存儲到哪里) Off表示設(shè)置為不使用Session功能; InProc表示將Session存儲在進(jìn)程內(nèi),這也是ASP中的存儲方式,這是默認(rèn)值; StateServer表示將Session存儲在獨(dú)立的狀態(tài)服務(wù)即ASP.NET State Service中; SQLServer表示將Session存儲在SQL Server。 cookieless(設(shè)置客戶端的Session信息存儲到哪里) true表示使用Cookieless模式(這表明SessionId將不再使用Cookie存儲了,而是將其通過URL存儲); false表示使用Cookie模式,這是默認(rèn)值。 從上面的設(shè)置配置中我們也可以發(fā)現(xiàn)Session和Cookie的關(guān)系: 首先Session在客戶端的實(shí)現(xiàn)肯定是SessionId; 默認(rèn)這個(gè)SessionId是通過Cookie存儲的(比較安全); 當(dāng)然也可以通過URL來進(jìn)行存儲,這樣Session和Cookie就沒有關(guān)系了,但是此種方式由于受URL長度限制以及明文傳送導(dǎo)致不安全而不被推薦使用。 Session過期時(shí)間設(shè)置,默認(rèn)為20分鐘。 stateConnectionString 如果設(shè)置將Session信息存儲在State Server中時(shí),則需要此配置字符串表明服務(wù)器名稱和端口。 sqlConnectionString 如果設(shè)置將Session信息存儲在SQL Server中,需此配置,表明數(shù)據(jù)庫的連接字符串,同時(shí)stateNetworkTimeout表明經(jīng)過多少秒空閑后,斷開Web服務(wù)器與存儲狀態(tài)信息的服務(wù)器的TCP/IP連接的。默認(rèn)值是10秒鐘。 三、Session服務(wù)器端配置 1. InProc顧名思義,此種模式表示Session將會被保存在內(nèi)存中,確切地說是保存在工作者進(jìn)程中,對于IIS 5而言是aspnet_wp.exe,對于IIS 6而言是w3wp.exe,設(shè)置方式如下(Web.config) <sessionState mode='InProc' cookieless='false' timeout='20'/> 由于是直接保存在進(jìn)程中,所以性能最好,但是經(jīng)常會發(fā)生Session信息丟失,常見的導(dǎo)致進(jìn)程重啟的可能情況為: 配置文件中processModel標(biāo)簽的memoryLimit屬性; Global.asax或者Web.config文件被更改; Bin文件夾中的Web程序(DLL)被修改; 殺毒軟件掃描了一些.config文件; 系統(tǒng)資源緊張進(jìn)行資源回收導(dǎo)致IIS進(jìn)程崩潰或重啟等。 2. State Server 此種方式是將Session信息存儲在其它的進(jìn)程中而不是IIS中,這樣就可以避免因IIS進(jìn)程崩潰或重啟而導(dǎo)致的Session信息丟失。但是此種方式要求保存在Session的信息必須序列化,然后從Session中獲取的時(shí)候也要反序列化,這就導(dǎo)致性能有略微的損失。 <sessionState mode='StateServer' cookieless='false' timeout='20'/> StateServer是本機(jī)的一個(gè)服務(wù),可以在系統(tǒng)服務(wù)里看到服務(wù)名為ASP.NET State Service的服務(wù),默認(rèn)情況是不啟動(dòng)的。當(dāng)我們設(shè)定mode為StateServer之后,請把該服務(wù)的啟動(dòng)模式設(shè)置為自動(dòng)(這樣下次服務(wù)就可以隨機(jī)器而啟動(dòng))并手工將該服務(wù)啟動(dòng)運(yùn)行。這樣,我們就能利用本機(jī)的StateService來存儲Session了,除非電腦重啟或者StateService崩掉,否則Session是不會丟的。 <sessionState mode='StateServer' stateConnectionString='tcpip=127.0.0.1:42424' cookieless='false' timeout='20'/> 此種配置和上面是一樣的(多了個(gè)stateConnectionString,換句話說127.0.0.1是默認(rèn)本機(jī)IP,42424默認(rèn)是該服務(wù)的訪問端口號,寫不寫效果是一樣的),都表示StateServer是在本機(jī)(注:StateServer模式是支持遠(yuǎn)程主機(jī)服務(wù)的,配置類似與下面) <sessionState mode='StateServer' stateConnectionString='tcpip=10.7.10.87:42424' cookieless='false' timeout='20'/> 注:如果在啟動(dòng)ASP.NET State Service服務(wù)時(shí)遇到問題0x8007277a 即無法啟動(dòng)或初始化,請嘗試在命令行(CMD)中輸入netsh winsock reset(有可能是winsock的問題,所以需reset一下) 3. SQL Server 此種方式是把Session信息保存在SQL Server的數(shù)據(jù)庫中,也需要序列化,性能有較大損失,但是Session一般不會發(fā)生丟失的情況,除非SQL Server宕機(jī)。而且此種方式也可以實(shí)現(xiàn)在Web Farm中的Session信息共享(上面兩種方式都不可以)。 <sessionState mode='SQLServer' sqlConnectionString ='data source=10.7.11.114; user id=session_user; password=Session@Pwd' timeout='20' /> 3.1 安裝ASPState數(shù)據(jù)庫 在使用之前,我們要安裝配置對應(yīng)的數(shù)據(jù)庫,而微軟給我們提供了一整套方案(你也可以選擇使用自己的數(shù)據(jù)庫或自己實(shí)現(xiàn)配置和管理)。ASP.NET 2.0版本后微軟提供了aspnet_regsql.exe工具可以方便的配置Session數(shù)據(jù)庫,該工具位于 Web 服務(wù)器上的'系統(tǒng)根目錄\Microsoft.NET\Framework\版本號'文件夾中. 使用舉例:aspnet_regsql.exe -S . -U session_user -P Session@Pwd -ssadd -sstype p -S參數(shù):表示數(shù)據(jù)庫實(shí)例名稱. 可以用'.'表示本機(jī),你也可以指定機(jī)器,如10.7.11.110等 -U和-P參數(shù):表示用戶名和密碼. -E參數(shù):可以再-U –P 與 -E中選擇一組. –E表示以當(dāng)前系統(tǒng)用戶通過windows身份驗(yàn)證登錄數(shù)據(jù)庫, -U -P則是使用SqlServer用戶登錄數(shù)據(jù)庫. -ssadd / –ssremove 參數(shù): -ssadd表示是添加Session數(shù)據(jù)庫, -ssremove表示移除Session數(shù)據(jù)庫. sstype 參數(shù):選項(xiàng) 說明 t 將會話數(shù)據(jù)存儲到 SQL Server tempdb 數(shù)據(jù)庫中。這是默認(rèn)設(shè)置。如果將會話數(shù)據(jù)存儲到 tempdb 數(shù)據(jù)庫中,則在重新啟動(dòng) SQL Server 時(shí)將丟失會話數(shù)據(jù)。 p 將會話數(shù)據(jù)存儲到 ASPState 數(shù)據(jù)庫中,而不是存儲到 tempdb 數(shù)據(jù)庫中。 c 將會話數(shù)據(jù)存儲到自定義數(shù)據(jù)庫中。如果指定 c 選項(xiàng),則還必須使用 -d 選項(xiàng)包括自定義數(shù)據(jù)庫的名稱。 注意:如果sstype為t,則在下面的用戶權(quán)限賦予中要授予對tempdb的dbowner權(quán)限,否則將無法操作數(shù)據(jù)庫。 3.2 建立連接數(shù)據(jù)庫 ASPState 的用戶,并為此用戶授權(quán) 運(yùn)行 SQL Server 的企業(yè)管理器 展開數(shù)據(jù)庫的安全性 右擊'登錄' 新建'登錄' 輸入'名稱' 選擇 'SQL Server 身份驗(yàn)證' 輸入'密碼' 指定'數(shù)據(jù)庫' 點(diǎn)擊'數(shù)據(jù)庫訪問' 勾選 'ASPState' 選中'db_owner'角色 點(diǎn)擊'確定' 再一次輸入'密碼' 點(diǎn)擊'確定' 后即可建立 ASPState 的用戶,下面用命令實(shí)現(xiàn): --新建數(shù)據(jù)庫賬號 SessionStateUser ,默認(rèn)登錄 ASPState use ASPState --切換 DataBase 3.3 啟動(dòng)SQL Server Agent 自動(dòng)運(yùn)行Job ASPState_Job_DeleteExpiredSessions刪除過期的Session,否則數(shù)據(jù)庫中的數(shù)據(jù)將一直增長。 四、Session客戶端配置 再次說明,Session的實(shí)現(xiàn)分為兩個(gè)部分:客戶端和服務(wù)器端,其中客戶端負(fù)責(zé)產(chǎn)生SessionId,服務(wù)器端負(fù)責(zé)保存具體的內(nèi)容,這兒所說的Session客戶端配置其實(shí)是想說說關(guān)于SessionId的一些東西。 1. Session在客戶端是如何實(shí)現(xiàn)的? 前面已經(jīng)說過,Session在客戶端是通過產(chǎn)生SessionId來實(shí)現(xiàn)的,至于這個(gè)SessionId又是通過什么方式回傳之服務(wù)器從而獲得具體的Session內(nèi)容,前面也略有說明,兩種方式:Cookie和Url,由于Url的方式會導(dǎo)致安全性問題,所以現(xiàn)在一般已不再使用此種方式。 當(dāng)系統(tǒng)是用Session的時(shí)候,系統(tǒng)將自動(dòng)在客戶端產(chǎn)生一個(gè)Cookie,名稱為ASP.NET_SessionId,為了便于區(qū)別,我們暫且將此Cookie稱為Session Cookie 2. Session Cookie何時(shí)失效? 和一般的Cookie一樣,默認(rèn)是保存在瀏覽器的內(nèi)存中,當(dāng)瀏覽器關(guān)閉時(shí)失效,如果想此Cookie保存在本地磁盤,可通過設(shè)置其Expires屬性來達(dá)到,這種方式現(xiàn)在也被廣泛應(yīng)用于網(wǎng)站的登錄,即用戶在登錄的時(shí)候選擇記住我或保持登錄一段時(shí)間,這樣當(dāng)用戶下次再次訪問的時(shí)候就無需再次輸入用戶名密碼從而達(dá)到快速訪問的目的 HttpCookie httpCookie = new HttpCookie('ASP.NET_SessionId', Request.Cookies['ASP.NET_SessionId'].Value); httpCookie.Expires = DateTime.Now.AddMinutes(20); Response.Cookies.Add(httpCookie); if (!string.IsNullOrEmpty(Request.Cookies['ASP.NET_SessionId'].Value)) { //...處理相關(guān)登錄驗(yàn)證等信息 Response.Redirect('Default.aspx?UserName=' loginUser.UserName ''); } 3. Session Cookie失效時(shí)Session失效嗎? 答案當(dāng)然是否定的,記?。篠ession Cookie和Session的失效沒有任何必然的聯(lián)系,因?yàn)樗鼈兪У幕鶞?zhǔn)或者條件根本不一樣,Session Cookie的失效時(shí)間取決于客服端Cookie的失效時(shí)間,如果是保存在瀏覽器中,那么關(guān)閉瀏覽器Session Cookie就將失效,否則如果保存在本地磁盤,則取決于該Session Cookie設(shè)置的過期時(shí)間;而服務(wù)器端的Session是保存在服務(wù)器端的,它的失效與否與其服務(wù)器端的設(shè)置和Session的過期時(shí)間有關(guān)(下面的討論將忽略過期時(shí)間這個(gè)因數(shù),你懂的),如果是InProc方式,那么當(dāng)承載的IIS進(jìn)程如果奔潰或重啟等都會丟失;如果是State Server,同樣如果這個(gè)Service宕掉,那Session也會丟失;而如果是SQL Server,則會寫入數(shù)據(jù)庫,如果你不刪除它,它甚至可以一直存在而永不丟失(事實(shí)是SQL Server Agent會自動(dòng)運(yùn)行一個(gè)Job刪除過期的Session);而當(dāng)如果我們使用自定義的數(shù)據(jù)庫來保存Session時(shí),你將獲得充分的控制。 4. Session Cookie何時(shí)產(chǎn)生?刷新頁面其值會改變嗎? 牢記一點(diǎn),只有當(dāng)使用Session的時(shí)候才會產(chǎn)生Session Cookie。你也許會反問那為什么當(dāng)配置Session模式為SQLServer時(shí),就算沒有使用Session,也可以獲得ASP.NET_SessionId的值,即在Page_Load的時(shí)候執(zhí)行方法Response.Write(Request.Cookies['ASP.NET_SessionId'].Value),會輸出值,我的猜測是雖然你可以獲得ASP.NET_SessionId值,但實(shí)際上并沒有真正產(chǎn)生Session Cookie,因?yàn)榇藭r(shí)當(dāng)我試圖通過HttpWatch(下面介紹的工具)來查看此Session Cookie的時(shí)候根本查不到,當(dāng)然這也只是我的個(gè)人猜測而已,至于具體的內(nèi)部機(jī)制還是不甚了解。 當(dāng)沒有使用Session的時(shí)候刷新頁面Session Cookie的值會變化嗎?答案是會的,測試方法如上,設(shè)置Session模式為SQLServer,在Page_Load的時(shí)候輸出Session Cookie的值,然后一直F5頁面即可,你會看到其值一直在變化,直到在代碼中明確地使用Session后便不再變化。 public partial class Login : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Response.Write(Request.Cookies['ASP.NET_SessionId'].Value); } protected void tbnLogin_Click(object sender, EventArgs e) { if (!string.IsNullOrEmpty(this.txtUserName.ToString()) && !string.IsNullOrEmpty(this.txtPassword.ToString())) { LoginUser loginUser = new LoginUser(); loginUser.UserId = Guid.NewGuid().ToString('N'); loginUser.UserName = this.txtUserName.Text; loginUser.UserPassword = this.txtPassword.Text; Session['LoginUser'] = loginUser; } } } 5. Session Cookie如何查看 如果設(shè)置了Session Cookie的過期時(shí)間,則此Session Cookie會保存在本地磁盤,一般是在目錄C:\Documents and Settings\user\Local Settings\Temporary Internet Files,此目錄可手工設(shè)置(Internet選項(xiàng)->常規(guī)->瀏覽歷史記錄中的'設(shè)置'選項(xiàng)->Internet臨時(shí)文件的'查看文件'選項(xiàng))。 如果沒有設(shè)置過期時(shí)間,則Session Cookie默認(rèn)是保存在瀏覽器內(nèi)存中的,Chrome瀏覽器默認(rèn)支持查看Cookie,具體步驟如下:Tools - > Settings ->Show Advanced Settings -> Privacy -> Content Settings -> Cookies -> All cookies and site data,不過坑爹的是它只支持查看和刪除,不支持修改,如果你想修改的話,我們只能通過第三方插件來完成https://chrome.google.com/webstore/detail/fngmhnnpilhplaeedifhccceomclgfbg(Chrome Web Store瀏覽下載安裝即可) 如果你使用的是IE瀏覽器,你也要通過安裝插件HttpWatch來查看(Firefox也適用) 五、Session的其它相關(guān)問題 1. 如何捕獲Global.asax中的Session_End()事件? |
|