Key/Value之王Memcached初探:三、Memcached解決Session的分布式存儲場景的應用


一、高可用的Session服務器場景簡介

1.1 應用服務器的無狀態特性

  應用層服務器(這里一般指Web服務器)處理網站應用的業務邏輯,應用的一個最顯著的特點是:應用的無狀態性

http

PS:提到無狀態特性,不得不說下Http協議。我們常常聽到說,Http是一個無狀態協議,同一個會話的連續兩個請求互相不了解,他們由最新實例化的環境進行解析,除了應用本身可能已經存儲在全局對象中的所有信息外,該環境不保存與會話有關的任何信息。之所以我們在使用ASP.NET WebForm開發中會感覺不到Http的無狀態特性,完全是因為Microsoft幫我們實現了ViewState,它是ASP.NET WebForm中保存頁面信息的基本單位,本質是一個HTML中的隱藏域,回調時會將這個隱藏域中的數據提交到服務器端。 

  在很多場景中,用戶都需要和我們的網站系統進行多次的信息交互,這時就需要一種解決方案來克服無狀態特性所帶來的困境。還好,在巨人的肩膀上,我們已經有了很好的解決方案,那就是瀏覽器端的Cookie服務器端的Session。在一般的單機開發中(這里一般是指只有一台Web服務器的情況),服務器端我們通常使用Session來存儲用戶登錄狀態(一般是一個自定義對象實例),在多數的管理信息系統開發中(畢竟內部系統用戶量不多,一台Web服務器既提供Web服務又存儲Session對象內存還算是夠用的)這是很常見的。

  但是,在大用戶量下,單機版的Session就會顯得效率低下,甚至會拖累Web服務器的性能。這是因為:每個用戶的Http請求發到服務器端后,每台Web服務器的服務器軟件(例如:IIS、Tomcat等)都會為該請求創建一個線程來進行處理和響應,但是一台服務器同一時間可以接收的請求數畢竟是有限的(這個根據服務器的配置而定,例如CPU中i3、i5和i7類型分別可以創建的線程數都各不相同),當某個時間段出現高並發請求數的時候(比如:網購秒殺系統中經常同一時間會出現海量的並發數),那這台應用服務器將會接收前所未有的請求負載,最終可能會因為承受不了高負載而導致宕機,網站不得不停止服務。

拉牛

  這時,又想起了那句話:當一頭牛拉不動車的時候,不要去尋找一頭更強壯的牛,而是用兩頭牛來拉車。於是,我們可以采用服務器集群的技術來對Web服務器進行改進,增加N台Web服務器部署相同的Web應用構成Web服務器集群來對外提供服務,通過負載均衡設備或軟件將海量的並發請求數平均地分攤到每台Web服務器,例如:假設某系統在促銷活動期間同一時刻涌入了10萬個請求,而服務器集群中有5台Web服務器同時提供服務,這時負載均衡設備就將這個10萬請求通過某種算法較為均衡地分配給其中的Web服務器,平均下來每台服務器最多就只承擔2萬個請求。

  通過服務器集群,已經較好地解決了請求負載問題,這時新的問題又來了:由於Session默認是屬於進程內(InProc)的,也就是說它是存儲在Web服務器的內存里邊的。當構建好集群之后,用戶的Session會建立在負載均衡設備所分配的其中一台Web服務器里邊。但是當用戶下一次訪問或者訪問系統中的其他子系統(比如:我首先在百度百科進行登錄了,然后訪問百度貼吧),由於Session會話還存儲在上一次提供服務的Web服務器里邊,系統校驗規則(現在這台Web服務器里邊檢測到沒有該用戶的Session)會造成用戶的重復登錄(比如:都是在百度的網頁,它卻讓你登錄好幾次,你爽嗎?很明顯,不爽吧)。這時,就需要我們解決Web服務器集群的Session管理,下面我們就來看看如何進行Web服務器集群的Session管理。

1.2 應用服務器集群的Session管理

  我們現在來看看在集群環境中,Session管理的幾種常見手段:

  ①Session復制:該方案簡單易行,集群中的幾台服務器之間同步Session對象,任何一台服務器宕機都不會導致Session對象的丟失,服務器也只需要從本機獲取即可。但是,該方案只適合集群規模較小的情況下。當規模較大時,大量的Session復制操作會占用服務器和網絡的大量資源,系統不堪重負

Session復制

  ②Session綁定:利用負載均衡的源地址Hash算法,總是將源於同一IP地址的請求分發到同一台服務器上。這樣的話,在整個會話期間,用戶所有的請求都在同一台服務器上進行處理,即Session綁定在某台特定服務器上,保證Session總能在這台服務器上獲取。(這種方案又叫做會話粘滯)。

Session綁定

  但是,這種方案不符合高可用的需求。因為一旦某台服務器宕機,那么該機器上得Session也就不復存在了,用戶請求切換到其他機器后因為沒有Session而無法完成業務處理。因此,很少有網站采用此方案進行Session管理。

  ③Cookie記錄Session:利用瀏覽器支持的Cookie記錄Session簡單易行,可用性高,並且支持服務器的線性伸縮,因此,許多網站都或多或少地使用了Cookie來記錄Session。但是Cookie記錄Session有缺點:比如受Cookie大小限制、每次請求響應都要傳輸Cookie影響性能、用戶關閉了Cookie會造成訪問不正常等。

Cookie記錄Session

  ④Session服務器:利用獨立部署的Session服務器(集群)統一管理Session,應用服務器每次讀寫Session時,都訪問Session服務器。這種方案實際上是將應用服務器的狀態分離,分為無狀態的應用服務器有狀態的Session服務器

Session服務器

    

  從上面的幾種方式來看,各有利弊,但Session服務器是最符合高可用需求的方案,也是企業中經常用到的方案。那么,對於有狀態的Session服務器,一種較簡單的方法是利用分布式緩存(如Memcached、Redis等,有關Redis的簡單介紹可以閱讀我的博文:NoSQL初探之人人都愛Redis)、數據庫等,在這些產品的基礎上進行封裝,使其符合Session的存儲和訪問要求。綜合上述介紹,我們今天就采用Memcached來構建我們的Session服務器,解決Web服務器集群的Session的共享訪問。

PS:為什么要采用分布式緩存方案而不采用數據庫來存儲Session?這個就得要分析一下數據訪問的性能瓶頸了,一般來說,磁盤IO讀寫的速度是最慢的,因為數據庫數據其實是存儲在文件中的,雖然目前大多數的數據庫都采用了B+樹結構,讀取一條數據最多都還是需要4次的數據讀寫(三次磁盤訪問獲得數據索引及行ID,一次數據文件讀操作,終於知道數據庫操作多麻煩了)。而分布式緩存例如Memcached是以Key/Value這種簡單的形式存儲在服務器的內存里邊的,內存的隨機讀寫速度是完爆磁盤IO的,因此內網+內存的雙內模式是比較完美的方案。

磁盤又分為兩種類型:

  ①機械硬盤:通過馬達驅動磁頭臂,帶動磁頭到指定的磁盤位置訪問數據。它能夠實現快速順序讀寫,慢速隨機讀寫

  ②固態硬盤(又稱SSD):無機械裝置,數據存儲在可持久記憶的硅晶體上,因此可以像內存一樣快速隨機訪問

  在目前的網站應用中,大部分應用訪問數據都是隨機的,這種情況下SSD具有更好的性能表現,但是性價比有待提升(蠻貴的,么么嗒)。

二、Memcached實現Session的分布式存儲

2.0 案例總體預覽

  (1)模擬的登錄案例場景

    假設我們有一個基於ASP.NET的信息系統,這個系統使用一個統一的系統登錄頁面進行用戶登錄,登陸后默認跳轉到一個用戶中心主頁,並顯示:歡迎您,{用戶賬號名稱}。

    ①系統登錄頁面效果:

    ②用戶主頁效果:

  (2)模擬的技術體系選擇

    ASP.Net MVC+EF Code First+MySQL+Memcached

2.1 初始准備工作

  (1)新建一個ASP.NET MVC4的空項目,視圖引擎選擇為“Razor”即可;

  (2)在項目中新建一個文件夾,取名為“Lib”,主要存放一些必要的DLL文件;

  (3)在項目中添加對這幾個DLL的引用,注意這里引入EntityFramework.dll是為了支持后面的CodeFirst開發方式,EF版本必須在4.1及以上。PS:你也可以通過Package Manager來安裝EntityFramework。到此,我們的准備工作就做好了,接下來就可以開始正式的工作了。

2.2 借助EF CodeFirst生成MySQL數據庫

  首先,EF是一種ORM(Object-relational mapping)框架,它能把我們在編程時使用對象映射到底層的數據庫結構。ORM框架負責把從數據庫傳回的記錄集轉換為對象,也可以依據對象當前所處的具體狀態生成相應的SQL命令發給數據庫,完成數據的存取工作(常見的數據存取操作可簡稱為CRUD:Create、Read、Update、Delete)。

  EF給數據庫應用系統開發帶來了更高的效率,使用它能更容易地寫出易維護、易擴展的系統,而且性能雖然比不上ADO.NET,但也足夠好,能滿足大多數開發場景的需求。與ADO.NET不一樣,EF的抽象層次較高它把數據庫映射為DbContext,把數據庫中存取的數據直接映射為實體(Entity)對象,屏蔽了底層的數據庫內部結構,無需直接使用下層數據存取引擎所提供的底層對象(比如ADO.NET所提供的DbConnection,DbCommands等)完成CRUD。

  EF支持三種開發模式:Code First、Database First和Model First。這里我們使用Code First模式,它能幫助我們實現快速開發迭代的目標。最后,EF不是本文的重點,如果你還不了解EF或者Code First,可以參閱金旭亮老師的《EF走馬觀花》系列文章,這里就不再贅述了。

  (1)在Models文件夾新建一個類,取名為“UserInfo”。它作為我們的實體類,映射到MySQL數據庫中的UserInfo表(這里MySQL數據庫中還未創建這樣的數據表)

    [Serializable]
    public class UserInfo
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }

        [Required]
        [MaxLength(128)]
        public string UserName { get; set; }

        [Required]
        [MaxLength(32)]
        public string UserPwd { get; set; }
    }
View Code

  (2)再在Models文件夾新建一個類,取名為“MyDbContext”,使其繼承於DbContext,作為EF操作的數據庫上下文使用。需要注意的是:這里的name=MySqlDemo,MySqlDemo為數據庫的名字。而這里this.Database.CreateIfNotExists()方法則用於判斷MySqlDemo這個數據庫是否已存在?如果不存在,那么就創建一個。

    public class MyDbContext : DbContext
    {
        public MyDbContext()
            : base("name=MySqlDemo")
        {
            this.Database.CreateIfNotExists();
        }

        public virtual DbSet<UserInfo> UserInfo { get; set; }
    }
View Code

  (3)在Web.config中新增一個數據庫連接字符串,database設置為“MySqlDemo”,與上面的MyDbContext中的構造函數中的name保持一致。

 <connectionStrings>
    <!--設置對MySql的鏈接字符串(注意:charset=utf8是為了解決MySql中保存中文的時候出現的亂碼的問題) -->
    <add name="MySqlDemo" connectionString="server=127.0.0.1;user id=root;password=1234;persist security info=True;database=MySqlDemo;charset=utf8;" providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
View Code

  至此,因為我們只用到UserInfo這一個表,所以現在我們有關數據庫與EF的代碼就到此結束。

2.3 自己封裝Memcached幫助類對外提供服務接口

  (1)在Web.config中新增一個AppSetting,保存Memcached服務器的地址列表:

    <!-- 設置Memcached服務器集群列表 -->
    <add key="MemcachedServers" value="192.168.80.10:11211,192.168.80.11:11211"/>

  (2)在Models中新建一個類,取名為“MemcachedHelper”,我們將其設置為靜態類,所以方法全是靜態的,我們無需實例化便可直接調用。可以看到,我們這里使用了靜態構造函數來初始化全局靜態對象,它不屬於任何一個實例,所以這個構造函數只會被執行一次,而且是在創建此類的第一個實例或引用任何靜態成員之前,由.NET自動調用。

    /// <summary>
    /// 自己封裝Memcached的幫助類
    /// </summary>
    public static class MemcacheHelper
    {
        #region 全局靜態對象
        // 全局Socket連接池對象
        private readonly static SockIOPool sockIOPool;
        public static SockIOPool CurrentPool
        {
            get
            {
                return sockIOPool;
            }
        }
        // 全局Memcached客戶端對象
        private static MemcachedClient mc; 
        #endregion

        #region 靜態構造函數
        /// <summary>
        /// 靜態構造函數
        /// </summary>
        static MemcacheHelper()
        {
            // 初始化Memcached服務器列表
            string[] serverList = ConfigurationManager.AppSettings["MemcachedServers"].Split(',');
            // 初始化Socket連接池
            sockIOPool = SockIOPool.GetInstance("MemPool");
            sockIOPool.SetServers(serverList);
            sockIOPool.Initialize();
            // 初始化Memcached客戶端
            mc = new MemcachedClient();
            mc.PoolName = "MemPool";
            mc.EnableCompression = false;
        } 
        #endregion

        #region 封裝對外方法
        /// <summary>
        /// Set-新增或修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns>是否成功</returns>
        public static bool Set(string key, object value)
        {
            if (!mc.KeyExists(key))
            {
                return mc.Add(key, value);
            }
            else
            {
                return mc.Set(key, value);
            }
        }

        /// <summary>
        /// Set-新增或修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry">過期時間</param>
        /// <returns>是否成功</returns>
        public static bool Set(string key, object value, DateTime expiry)
        {
            if (!mc.KeyExists(key))
            {
                return mc.Add(key, value, expiry);
            }
            else
            {
                return mc.Set(key, value, expiry);
            }
        }

        /// <summary>
        /// Get-獲取
        /// </summary>
        /// <param name="key"></param>
        /// <returns>具體數據</returns>
        public static object Get(string key)
        {
            if (!mc.KeyExists(key))
            {
                return null;
            }
            else
            {
                return mc.Get(key);
            }
        } 
        #endregion
    }
View Code

  這里我們沒有對SockIOPool進行一列的配置,采用其默認的配置即可。但是要注意的是:SockIOPool的PoolName和MemcacheClient的PoolName必須保持一致。

2.4 用戶登錄時調用Memcached幫助類接口存儲用戶登錄狀態

  (1)在Controller中新建一個控制器,取名為“LogonController”。主要用來顯示系統登陸頁和進行用戶驗證的AJAX操作(將用戶Session存入Memcached也在此操作中)。至於登錄頁面的HTML和JS腳本在此就不再贅述,請自行下載Demo文件查看。

    public class LogonController : Controller
    {
        //
        // GET: /Logon/
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public JsonResult ValidateUserInfo(string userName, string userPwd)
        {
            MyDbContext dbContext = new MyDbContext();
            var loginUser = dbContext.UserInfo.Where(u => u.UserName.Equals(userName)).FirstOrDefault();

            JsonObject jsonReturn = new JsonObject();
            if (loginUser == null)
            {
                jsonReturn.success = false;
                jsonReturn.msg = "您輸入的用戶名不存在!";
            }
            else
            {
                if (!loginUser.UserPwd.Equals(userPwd))
                {
                    jsonReturn.success = false;
                    jsonReturn.msg = "您輸入的密碼錯誤!";
                    return Json(jsonReturn, JsonRequestBehavior.DenyGet);
                }

                // 申請了一個模擬的Guid:SessionId
                Guid sessionId = Guid.NewGuid();
                // 把sessionid寫到客戶端瀏覽器里面去累
                Response.Cookies["sessionId"].Value = sessionId.ToString();
                // 將用戶實體對象寫入到Memcache中
                MemcacheHelper.Set(sessionId.ToString(), loginUser, DateTime.Now.AddMinutes(20));
                // 設置成功響應消息
                jsonReturn.success = true;
                jsonReturn.msg = "恭喜您,登錄成功!";
            }

            return Json(jsonReturn, JsonRequestBehavior.DenyGet);
        }
    }
View Code

  現在來看下這個控制器的核心代碼:

  ①Index這個Action主要用於顯示登錄視圖,頁面代碼不再貼出來,只看看下面這個AJAX請求代碼:借助JQuery AJAX向ValidateUserInfo這個Action提交用戶名和密碼,如果服務器端校驗成功則返回一個JSON對象,瀏覽器端判斷success屬性是否為true,如果為true則跳轉到系統主頁。

    $.ajax({
                type: "POST",
                contentType: "application/json;charset=utf-8",
                url: "/Logon/ValidateUserInfo",
                data: "{ userName:'" + txtUserName + "',userPwd:'" + txtUserPwd + "' }",
                dataType: "json",
                beforeSend: function () {
                    showMsg("操作提示", "正在驗證中,請稍候...");
                },
                success: function (resultInfo) {
                    if (resultInfo != null && resultInfo.success) {
                        window.location.href = "/Home/Index";
                    }
                    else {
                        showMsg("登錄失敗", resultInfo.msg);
                    }
                },
                error: function () {
                    showMsg('錯誤提示', '發生未知錯誤');
                }
            });
View Code

  ②ValidateUserInfo這個Action則是根據瀏覽器提交過來的AJAX請求,判斷用戶名和密碼是否正確。這里用到了我們剛剛寫的MyDbContext,首先對它進行實例化。(這一步非常重要,這時我們的MySQL數據庫中還木有MySqlDemo這個數據庫,當第一次實例化MyDbContext時,EF會幫我們在MySQL中創建MySqlDemo這個數據庫,其本質其實就是幫我們生成一串:crate database mysqldemo;之類的SQL語句),然后使用Lambda表達式這種優美的語法去進行校驗。如果你對Lambda表達式不熟悉,可以參閱MSDN的《Lambda表達式(C#編程指南)》一文來學習下。

  ③就是最核心的部分,我們采用Guid(保證作為Key的唯一性)作為SessionId寫入瀏覽器端的Cookie中,並以此作為Key存入Memcached分布式緩存中,還給它設置了默認失效時間(這里為20分鍾)。之后,每次瀏覽器向服務器端提交請求時,在HTTP報文中都會附帶上這個Cookie,服務器端就可以通過這個Cookie作為Key去Memcached服務器中查找Session對象。

PS:一般來說,Memcached的數據Key的命名是有講究的,這里傳智的老馬推薦了一個命名規則:{命名空間}-{部門名稱}-{項目名稱}。我們這里就簡單一點,使用Guid來作為Key,因為每次生成的Guid是唯一的。

2.5 封裝BaseController解決Action觸發前的校驗規則

  (1)在以往的信息系統項目開發中,我們在系統里邊會做一個全局的校驗器,判斷用戶的每次操作請求是否具有相應的權限,這里我們主要是校驗用戶是否登錄,以便我們在具體的模塊中獲取用戶的Session對象。單機情況下,我們一般就存入本機進程內的Session,因此就主要判斷Session中是否存在登錄狀態。這里我們使用Memcached來存儲Session對象,那么我們就在每個Action執行前加一段規則:判斷Memcached中是否有當前用戶的登錄狀態,如果有,則繼續執行Action。如果木有,那么對不起,請進行登錄

  (2)在WebForm中我們可以通過寫一個BasePage,使其繼承於Page,重寫OnLoad事件,再讓其他頁面繼承於BasePage,就可以對用戶是否登錄這個校驗應用到所有繼承了BasePage的Page類中。那么,在MVC模式中,請求對象不再是xxx.aspx頁面類型,而是/ControllerName/ActionName的路由,因此我們需要尋找一種針對Action的全局過濾方法。額,在此我們不禁想到了一個高大上的英文:AoP(Aspect Oriented Programming),它是軟件開發中一個較為熱門的話題,利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。因此,我們對Action增加全局校驗規則就是將Action的校驗方法與Action的業務處理相分離,使其耦合度降低,也提高了Action的校驗方法的重用性,符合了AoP的思想。那么,扯了大半天,在ASP.NET MVC中到底如何實現呢?別擔心,我們可以像寫BasePage一樣,寫一個BaseController來進行處理,使其繼承於Controller,然后將其他需要使用用戶登錄狀態校驗的Controller都繼承於BaseController即可。

    public class BaseController : Controller
    {
        // 全局當前用戶對象
        protected UserInfo CurrentUser { get; private set; }

        // 全局Action執行前的驗證操作:類似於AoP的思想,面向切面編程
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);

            // 從cookie中獲取登錄的sessionId
            string sessionId = Request["sessionId"];
            if (string.IsNullOrEmpty(sessionId))
            {
                filterContext.Result = new RedirectResult("/Logon/Index");
            }
            else
            {
                object obj = MemcacheHelper.Get(sessionId);
                CurrentUser = obj as UserInfo;
                if (CurrentUser == null)
                {
                    filterContext.Result = new RedirectResult("/Logon/Index");
                }
                // 再次記錄到Memcache分布式緩存中:實現Session的滑動窗口機制
                MemcacheHelper.Set(sessionId, CurrentUser, DateTime.Now.AddMinutes(20));
            }
        }
    }
View Code

  (3)現在來看看上面這段代碼:

  ①從瀏覽器提交過來的HTTP請求報文中取得Cookie(也就是SessionId),如果沒有Cookie那么肯定還沒有登錄,將進行重定向到登陸頁面;

  ②如果有Cookie那么就去Memcached服務器中查詢是否有這個Key的數據內容,如果沒有沒有內容,那么還是重定向到登陸頁面;如果有內容,則返回這個對象,由於返回的是Object類型,所以需要我們轉換一下賦給全局的用戶對象實例。

  ③獲取到UserInfo對象之后,再次將其重新存儲到Memcached中,這里其實是將Session延長失效時間,實現了一個Session的滑動時間機制的效果。

PS:因為我們在登錄驗證的Action里面給這個緩存設置的過期時間是一個絕對的時間,而非滑動過期時間。所謂絕對時間是指到了指定時間以后便會失效,而滑動時間是指在指定時間內無訪問請求便失效

2.6 測試運行用戶登錄與Session存儲

  (1)到此,大部分的代碼都已完畢。這里我們需要進行一個小測試,新建一個Controller,取名為“HomeController”,將Index頁面用作登陸后的主頁,顯示Session對象中的UserName屬性。代碼很簡單,只有兩行:

        public ActionResult Index()
        {
            // 從Memcached中獲取Session並傳給View
            ViewBag.UserName = CurrentUser.UserName;
            return View();
        }    

  至於View,請參考Demo文件,此處就不在贅述了,就一堆HTML而已。

  (2)①開啟虛擬機中的兩台Windows Server服務器,確保Memcached服務都已成功開啟。(如果你是裝在本機的,直接開啟服務即可)

      ②開啟MySQL數據庫服務,這里我的MySQL是裝在本機的,所以只需要開啟服務即可。

  (3)點擊調試,開始運行IIS Express服務器,進行測試吧騷年(這里需要說明的是:你需要首先隨便輸入一個賬號和密碼進行數據庫的創建,因為我們采用的是Code First的方式,然后再進入MySQL添加幾行測試用戶數據,這里我已經添加了幾行,開始進行測試。)我在數據庫里邊添加了一行{賬號:edisonchou,密碼:123456},因此我輸入賬號和密碼,點擊登錄,會出現一個友好的提示:“正在驗證中,請稍候...”(如果你是第一次運行,那么需要創建數據庫,這個時間會比較長,不要擔心)。

  (4)等待系統驗證完成后,便會跳轉到系統主頁,顯示歡迎界面。可以看到,下圖中的兩處是根據Session的UserName來顯示的。

  (5)現在我們手動修改URL,輸入/Logon/Index,跳轉到登陸頁面。然后,再修改URL為/Home/Index,看看是否需要重新登錄,由於我們已經將Session存入了Memcached,所以再次進入/Home/Index這個Action時會進行判斷,通過瀏覽器端傳過去的Cookie去Memcached中取Session,如存在則繼續執行。這里,我們之前已經登錄了,所以不再需要重新登錄。下面是再次跳轉/Home/Index的HTTP報文信息,可以看出登錄之后已經寫入了Cookie,之后的請求都會帶上Cookie傳遞到服務器端,服務器端也就可以通過Cookie作為Key獲取Session對象。

  (6)新開一個瀏覽器,再使用另一個用戶數據進行登錄:

  (7)登陸后,查看HTTP報文,獲取Cookie中的sessionId:

  (8)根據兩個用戶的sessionId作為Key,在Memcached服務器中查看存儲情況:經查看,兩個Session都存儲到了192.168.80.11這台服務器里邊。為何沒有平均分配,這是因為算法是根據計算Hash值來的,剛好這兩個sessionId計算后的Hash值都是要分配到192.168.80.11這台服務器里邊,所以就都存到了這里邊。(需要注意的是:這里僅僅是在我的機器上跑的測試結果,網友們的測試結果可能會跟我的不一樣,這是正常的)

  至此,我們的小測試到此結束,我們的案例也到此為止。

三、學習小結

  本篇我首先通過花大力氣對Session服務器場景的簡介引出分布式緩存對於構建Session服務器的可行性,然后使用ASP.NET MVC+EF Code First+MySQL+Memcached來模擬了一個信息系統登錄場景,其中借助了Memcached來存儲Session對象,並通過Action的過濾器來實現對用戶登錄狀態的校驗。最后通過一個小測試,來驗證Memcached是否存儲了我們的Session對象。

  當然,此案例只是一個玩具級別的Demo,還需要經過很多次性能測試和優化才能應用到實際開發中。但是,我們可以從中了解到一些有益的思想,理解這些思想對於我們的實際開發能力有所裨益的。最后,希望我的文章能夠給更多的像我一樣的菜鳥開發者帶來一點光明,如果你覺得有用,那就麻煩點一下“推薦”吧,謝謝!

參考文獻

    (1)李智慧,《大型網站技術架構-核心原理與案例分析》,http://item.jd.com/11322972.html

  (2)馬倫,《Memcached公開課》,http://bbs.itcast.cn/thread-14836-1-1.html

  (3)旋風,《Memcached分布式緩存替代Session解決方案》,http://www.cnblogs.com/xuanfeng/archive/2009/06/04/1494735.html

  (4)金旭亮,《Entity Framework走馬觀花》,http://blog.csdn.net/bitfan/article/details/12779517

  (5)MSDN,《靜態類和靜態成員(C#編程指南)》,http://msdn.microsoft.com/zh-cn/library/79b3xss3.aspx

  (6)痞子一毛,《Memcached+Cookie替代Session解決方案(MVC版)》,http://www.cnblogs.com/piziyimao/archive/2013/01/29/2882236.html

  (7)瞌睡龍飛,《.NET中Cache的用法》,http://blog.csdn.net/ttotcs/article/details/7476234

  (8)lulu Studio,《ASP.NET MVC:Action過濾器(Filtering)》,http://www.cnblogs.com/QLeelulu/archive/2008/03/21/1117092.html

附件下載

  (1)MemcachedMvcDemo:http://pan.baidu.com/s/1gd3RvKB

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM