標籤:
本文轉自:http://www.cnblogs.com/zhangziqiu/archive/2009/03/26/SessionServer.html
一.摘要
所有Web程式都會使用Session儲存資料. 使用獨立的Session伺服器可以解決負載平衡情境中的Session共用問題.本文介紹.NET平台下建立Session伺服器的幾種辦法, 並介紹在使用Session時的各種經驗和技巧.
二.關於Session,SessionID和Cookies
Session資料儲存在伺服器端, 但是每一個用戶端都需要儲存一個SessionID, SessionID儲存在Cookies中, 關閉瀏覽器時到期.
在向伺服器發送的HTTP請求中會包含SessionID, 伺服器端根據SessionID擷取擷取此使用者的Session資訊.
很多初級開發人員不知道SessionID和Cookies的關係, 所以常常認為兩者沒有聯絡. 這是不正確的. 正是因為SessionID儲存在Cookies中, 所以在我們儲存Cookies的時候,一定要注意不要因為Cookies的大小和個數問題而導致SessionID對象. 在我們的程式中, 對SessionID的Cookies有特殊的處理:
/// <summary> /// 寫入cookie. /// </summary> /// <param name="day"></param> /// <returns></returns> public bool SetCookie(int day) { string CookieName = GetType().ToString(); HttpCookie SessionCookie = null; //對 SessionId 進行備份. if (HttpContext.Current.Request.Cookies["ASP.NET_SessionId"] != null) { string SesssionId = HttpContext.Current.Request.Cookies["ASP.NET_SessionId"].Value.ToString(); SessionCookie = new HttpCookie("ASP.NET_SessionId"); SessionCookie.Value = SesssionId; }
//省略掉中間的代碼部分.只保留備份SessionID和找回SessionID的邏輯
//如果cookie總數超過20 個, 重寫ASP.NET_SessionId, 以防Session 丟失. if (HttpContext.Current.Request.Cookies.Count > 20 && SessionCookie != null) { if (SessionCookie.Value != string.Empty) { HttpContext.Current.Response.Cookies.Remove("ASP.NET_SessionId"); HttpContext.Current.Response.Cookies.Add(SessionCookie); } } return true; }
三.搭建Session伺服器的幾種方式
將Session儲存在獨立的伺服器中可以實現在多台Web伺服器之間共用Session.雖然我們也可以自己開發Session儲存系統, 但是使用ASP.NET內建的儲存機制將更加便捷.
.NET提供了5種儲存Seission的方式:
方式名稱 |
儲存方式 |
效能 |
Off |
設定為不使用Session功能 |
無 |
InProc |
設定為將Session儲存在進程內,就是ASP中的儲存方式,這是預設值。 |
效能最高 |
StateServer |
設定為將Session儲存在獨立的狀態服務中。通常是aspnet_state.exe進程. |
效能損失10-15% |
SQLServer |
設定將Session儲存在SQL Server中。 |
效能損失10-20% |
Customer |
自定製的儲存方案 |
由實現方式確定 |
我們可以在Web.Config中配置程式使用的Session儲存方式.預設情況下是InProc, 即儲存在IIS進程中. 關於Off, InProc和Customer本文不做講解. 相關文章大家都可以在網上搜尋到.
下面主要講解 StateServer 和 SQLServer 的應用.
四.使用 StateServer 模式搭建Session伺服器(1)伺服器端配置
1.啟動 Asp.net State service服務.(這個服務預設的狀態為手動.修改為自動並啟動.)
2.修改註冊表: [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\aspnet_state\Parameters]
設定 AllowRemoteConnection = 1 , 設定 Port = 42424 (十進位,預設即為42424)
Port是服務的連接埠號碼
AllowRemoteConnection 表示是否允許其他機器串連,0為僅能本機使用,1為可以供其他機器使用.
(2)用戶端設定
在Web應用程式的Web.Config中, 我們需要修改 <configuration> / <system.web> 的<sessionState>節點.如果沒有
沒有則添加(預設使用的是InProc方式)
<sessionState mode="StateServer" stateConnectionString="tcpip=伺服器ip:42424" cookieless="false" timeout="60"/>
上面的參數我們可以根據需要修改.
五.使用SqlServer模式搭建Session伺服器(1)伺服器端配置
使用SqlServer模式搭建Session伺服器端有兩種方式. ASP.NET 1.0和1.1版本請使用方式a, 2.0即以上版本請使用方式b.
a.使用SQL檔案建立Session資料庫
在ASP.NET 1.0和1.1 版本中, 只能使用這種方式.對於2.0及其以上版本,請使用aspnet_regsql.exe工具.(當然此方法也通用2.0版本)
.net提供了資料庫安裝指令碼,可以在機器的windows檔案夾中找到:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlState.sql
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlStateTemplate.sql
根據ASP.NET的版本不同, 需要使用不同的SQL指令碼. ASP.NET主要有1.1和2.0兩個版本,可以在不同的版本檔案夾找到這兩個SQL.
InstallSqlState.sql 是建立預設名稱的資料庫"[ASPState]".此SQL可以直接運行.
InstallSqlStateTemplate.sql 可以使用自己指定的資料庫儲存資料.此SQL需要自己修改後運行, 開啟SQL檔案將其中 [DatabaseNamePlaceHolder] 替換為自己指定的資料庫名稱.
執行installsqlstate.sql時不需要指定資料庫,可以在任意資料庫上執行.此SQL會自己建立新的資料庫
b. 使用aspnet_regsql.exe工具
ASP.NET 2.0版本後微軟提供了aspnet_regsql.exe工具可以方便的配置Session資料庫.該工具位於 Web 服務器上的"系統根目錄\Microsoft.NET\Framework\版本號碼"檔案夾中.
使用舉例:
aspnet_regsql.exe -S . -U sa -P 123456 -ssadd -sstype p
-S參數:
表示資料庫執行個體名稱. 可以用"."表示本機.
-U和-P參數:
表示使用者名稱和密碼.
-E參數:
可以再-U –P 與 -E中選擇一組. –E表示以當前系統使用者通過windows身分識別驗證登入資料庫, -U -P則是使用SqlServer使用者登入資料庫.
-ssadd / –ssremove 參數:
-ssadd表示是添加Session資料庫, -ssremove表示移除Session資料庫.
sstype 參數:
選項 |
說明 |
t |
將會話資料存放區到 SQL Server tempdb 資料庫中。這是預設設定。如果將會話資料存放區到 tempdb 資料庫中,則在重新啟動 SQL Server 時將丟失會話資料。 |
p |
將會話資料存放區到 ASPState 資料庫中,而不是儲存到 tempdb 資料庫中。 |
c |
將會話資料存放區到自訂資料庫中。如果指定 c 選項,則還必須使用 -d 選項包括自訂資料庫的名稱。 |
(2)Session用戶端設定
此房是同樣需要Web應用程式修改Web.Config中的<sessionState>節點.如果使用預設的資料庫(ASPState庫), 則配置如下:
<sessionState mode="SQLServer" sqlConnectionString="server=192.168.9.151; uid=sa; pwd=123456;" />
如果使用了自訂的資料庫名稱,則還需要制定allowCustomSqlDatabase屬性並在資料庫連接串中指定資料庫:
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="server=192.168.9.151; DataBase=MyAspState;uid=sa; pwd=123456;" />
六.使用經驗與技巧總結
下面是SessionID, Session_End時間, StatServer模式 和 SqlServer模式的各種經驗和技巧總結.
(1)StateServer模式:
1.在web farm中,請確認在所有的web伺服器上有相同的<machineKey>
2. 要儲存在Session中的對象是可序列化的。
3.為了在web farm中的不同web伺服器上維護session state,IIS Metabase中的網站應用程式程式路徑(如\LM\W3SVC\2)應該在所有的伺服器上保持一致(大小寫敏感).
4. ASP.NET處理Session是在Machine.Config中配置的HttpModuel模組, 在.NET的安裝目錄下的Config檔案夾中, 查看Web.Config(1.1版本是在Machine.Config):
<httpModules> ...
<add name="Session" type="System.Web.SessionState.SessionStateModule"/> ...
</httpModules>
確認此模組是否存在.
5.StateServer不支援負載平衡, 所以如果大並發推薦使用SqlServer模式, 可以享受到SqlServer的高效能和安全性.雖然儲存效率會有下降.
6.需要讓所有機器的MachineKey相同.在Machine.Config中配置:
<machineKey validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" decryptionKey="123456789012345678901234567890123456789012345678" validation="SHA1" decryption="Auto" />
(2)SqlServer模式:
1. 要儲存在Session中的對象是可序列化的。
2. 如果使用了預設是資料庫, 則在用戶端設定檔中的資料庫連結字串的使用者,需要擁有ASPState和tempdb兩個庫的dbowner許可權.
3. 在SQLServer模式下,session到期是由SQL Agent使用一個註冊任務完成的,要確認SQL Agent已經運行。否則無法清理到期的Session資料, 會導致資料庫資料一直增加.
4. 如果使用SqlServer模式時, 對於Web場中的各伺服器的 ASP.NET 應用程式路徑必須是相同的。請在 IIS 設定資料庫中對 Web 場中的所有 Web 服務器進行 Web 網站的應用程式路徑同步。大小寫一定要相同,因為 Web 網站的應用程式路徑是區分大小寫。
5.需要讓所有機器的MachineKey相同.在Machine.Config中配置:
<machineKey validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" decryptionKey="123456789012345678901234567890123456789012345678" validation="SHA1" decryption="Auto" />
(3)Session:
1. 不能直接通過Session伺服器在ASP.NET和ASP之間共用Session. 請使用微軟提供的解決方案: http://msdn.microsoft.com/zh-cn/library/aa479313.aspx
2. 在不同的應用程式或一個網站的不同虛擬目錄之間無法共用Session
3. Session的到期時間是滑動時間.
4. Session儲存.NET內建的實值型別效能最優. 儲存物件會降低效能.
(4)SessionID:
1.SessionID 還可以儲存在URL上, 設定Web.Config檔案中的System.Web/sessionState節點的Cookiesless屬性即可:
<sessionState cookieless="UseUri" />
2. 一般在Session逾時或刪除之後,SessionID保持不變. 因為Session到期後會在伺服器端清除資料, 但是SessionID儲存在使用者瀏覽器上, 所以只要瀏覽器不關閉則HTTP頭中的SessionID保持不變.
3.關閉瀏覽器後再訪問, SessionID會不同.
4.每開啟一個IE6視窗, SessionID都不同, 在IE6中兩個視窗的Session不能共用.
5.FireFox的標籤頁和新的FireFox視窗, SessionID都相同, 在FF的視窗和標籤頁上Session能共用.
6.對於包含FrameSet的頁面,比如:
<frameset cols="25%,50%,25%"> <frame src="SessionID.aspx"> <frame src="SessionID.aspx"> <frame src="SessionID.aspx"></frameset>
如果尾碼名是.htm並且.htm檔案沒有交給ASP.NET的ISAPI處理, 那麼根據伺服器速度在每個Frame頁面產生不同的SessionID, 再重新整理後相同都等於最後一個SessionID.
解決辦法是將.htm尾碼改成.aspx, 或者將.htm檔案交給ASP.NET的ISAPI處理.
(5)Session_End事件:
1. Session_End僅在InProc模式中可用
2. 關閉瀏覽器,Session_End是不會觸發的。HTTP是一種無狀態協議,伺服器沒有辦法知道你的瀏覽器是否已經關閉。
3. 當Session因為時間到期或調用Session.Abandon時,Session_End才會觸發.Session.Clear()僅僅是清除資料,但沒有刪除session。
4. Session_End由一個後台線程觸發,使用工作者進程帳號運行. 所以程式不會通知發生的錯誤.
5. 在Session_End訪問資料庫要考慮許可權問題. Session_End是用運行工作者進程(aspnet_wp.exe)的帳號啟動並執行,這個帳號可以在machine.config中指定。因此,在Session_End中,如果使用integrity security串連SQL,它將使用工作者進程帳號身份串連,這可能會引起登入失敗.
6.因為Session_End是有獨立線程出發的, 所以在Session_End中無法使用HttpContext對象(Request,Response,Server等對象都在HttpContext中), 即無法使用 Response.Redirect 和Server.Transfer等方法.
七.總結
我已經使用SqlServer模式對公司的多台伺服器實現了Session共用, 伺服器重啟也不會導致使用者預定過程重新開始(預定過程需要的Session不會丟失). 希望本文對具體的Session伺服器搭建人員有所協助.
參考文獻:
使用一個 SQL 資料庫 SQL Server 工作階段狀態的所有應用程式可能導致瓶頸
http://support.microsoft.com/kb/836680/zh-cn
ASP.NET 中 Session 實現原理淺析 [1] /[2]
http://www.cnblogs.com/flier/archive/2004/08/04/30226.html
http://www.cnblogs.com/flier/archive/2004/08/07/30902.html
理解Session State模式+ASP.NET SESSION丟失FAQ [翻譯]
http://www.cnblogs.com/tonyqus/archive/2006/10/24/522618.html
[轉]Session伺服器配置指南與使用經驗