分布式應用首先要解決的是跨域的問題,解決session、frame、cookie的跨域是最基本的,然后才是負載均衡和性能優化,上面的不解決就沒法往后面進行。上一博客主要是解決了frame跨域的問題,今天來解決session跨域的問題,如何讓session共享。其實解決session共享的方案有好多,比如通過數據庫像redis、sqlserver,或者用session服務器,今天主要是通過sql server來實現session共享。
一、什么是cookie?
大家平時在登錄網頁時有一個記住用戶名的選項,等下次登錄會填充用戶名,甚至有的會自動填充用戶名和密碼,這樣就會很方便用戶。那它是怎么實現的呢?其實很簡單就是通過cookie保存在瀏覽器中,如下圖,這是163.com存在谷歌瀏覽器中的cookie,用戶在瀏覽器輸入url時,瀏覽器會將該域名下存儲的cookie放在http的head中發送到服務器(瀏覽器不能把其他域名的cookie也發送到服務器,不然這就亂套了,這么多cookie,什么還有重名的,於是乎這就產生了cookie的跨域,關於cookie的跨域請聽下文分解。)然后服務器給控件賦值,於是就達到了自動填充的效果。可能會覺得這樣不安全,人家能監聽到你的cookie信息,比如用戶名密碼這些,確實,這也是有一定的弊端,有的做的完善的會進行一些加密,但加密也是需要消耗額外的資源的,所以都是有利有弊吧。而且最重要的是瀏覽器每次請求都會將對應的cookie發送給服務器,服務器又會把cookie響應給客戶端,cookie內容多的時候,可想而知,系統就會變慢,所以性能優化有一條就是對cookie優化,性能優化又是一個新的大陸。
二、session和cookie的關系
關於什么是session,對我來說沒有十萬個為什么也有一二十個。它是怎么來的又是怎么沒的,HTTP本身是無狀態的,客戶端和服務端又是怎么個的眉來眼去的讓服務器知道是同一個用戶的操作呢?每個用戶登錄之后都會在服務器保存session["UserName"],那這么多的session["UserName"],怎么確保A用戶獲取的session["UserName"]就是它本人的,不是其他用戶的呢?對於每次請求都會實例化一個session對象,這個session對象有一個sessionId,當用戶設置session["UserName"]后,那這個session對象就會存在服務器中,如果session對象中沒有那就會被銷毀。保存在服務器中之后,響應給瀏覽器時會把這個session對象對應的sessionid放在cookie中,這樣瀏覽器下次再請求時就會把這個sessionid帶上,然后就能根據sessid找到這個session對象了,而不至於找到別的。在下圖可以找到sessionID,設置的session["UserName"]放在session對象的keys熟悉中。Mode時InProc模式,就是放在IIS中。
三、Session共享設置
要想共享,多個應用用一份,那就得先把session分離出來,不然怎么做到共享。
1.啟動ASP.NET State Service服務
2.在數據庫服務器執行sql
sql文件在C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallSqlState.sql,InstallSqlState.sql是注冊,UninstallSqlState.sql是取消。執行完之后只是創建了ASPState數據庫,並未創建表如下圖,還提示我使用aspnet_regsql.exe去install sql session state。以及啟動SQLServer Agent.
3.啟動SQLServerAgent
4.注冊SQL Session State
原本想着直接運行aspnet_regsql.exe注冊,但打開之后提示要用命令行,已經提示用此向導創建或配置成員、角色 事件的,對於回話狀態用命令行。
於是乎根據提示cmd命令行 aspnet_regsql.exe -?出現下面的使用方法
由於是我在本地的數據庫上所以執行的是我本地的數據庫服務器的名字:aspnet_regsql.exe -S PC-201611282159 -E -ssadd -sstype p
注冊完成之后ASPState數據庫就存在了兩張表:ASPStateTempApplications,ASPStateTempSessions
從表結構能看出來有appid和appname,這就導致了默認是一個應用一個id和name,那這樣是共享不了的,怎么共享呢?其實也比較容易,只需需改下存儲過程TempGetAppID中的WHERE AppName = @appName注釋掉,然后重啟就好了。
四、demo
這里我創建了兩個應用,一個是WebForm的,一個是MVC的,在WebForm中設置session,然后在MVC中獲取session。

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebForm { public partial class Index : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { Session["UserName"] = "CuiYW"; HttpCookie cookie = new HttpCookie("UserInfo"); Request.Cookies.Add(cookie); } } } }

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MVC.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { if (Session["UserName"] != null) { ViewBag.UserName = Session["UserName"]; } return View(); } } }
同時要在webconfig的<system.web>中都要配置
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="server=PC-201611282159; database=ASPState;Trusted_Connection=true;" timeout="20" >
先運行WebForm設置session,然后運行MVC,在View中顯示ViewBag.UserName.結果是可以獲取到,這就意味着實現了共享。
五、總結
上面主要是利用SessionStateMode的SQLServer來實現session共享,這畢竟是微軟的,所以具有一定的局限行,只能是sql server。其實session共享可以用其他的數據庫,比如redis,而且一般實際應用中也不會只用一台做服務器,最少有一台做備份。還有就是單點登錄的方式有好幾個,session共享也只是其中一個。這些都會在以后的博客中一一寫到。reids實現session、SSO是我最近的重點。之前對於分布式應用可能也沒多少實戰,可能主要還是懶,用不到就是了解了解看看書,只有理論沒有實踐,通過這次將WebForm集成到MVC中,算是動手了一下,也讓自己學到很多東西。最后想吐槽一下,寫博客真不容易,不僅要寫還要邊寫邊操作,為了這篇搞了一上午,下午午休之后又開始搞,直接搞到現在。生活不易啊!!!