天天看點

Session分布式共享 = Session + Redis + Nginx

一、Session

1、Session 介紹

      我相信,搞Web開發的對Session一定再熟悉不過了,是以我就簡單的介紹一下。

      Session:在計算機中,尤其是在網絡應用中,稱為“會話控制”。 每個使用者(浏覽器)首次與web伺服器建立連接配接時,就會産生一個Session,同時伺服器會配置設定一個SessionId給使用者的浏覽器。我們可以用Fiddler檢視cookies中,會看到有一個ASP.Net_SessionId的cookie。大家都知道Http是無狀态請求,但是ASP.Net中的Session仿佛又讓Http請求變得有狀态,其核心就在于這個叫ASP.Net_SessionId的cookie。大家可以想象一下,這個相當于資料庫的Key,伺服器那邊再有個Session内容緩存表,是不是Session的内容就很容易得到了?當然Session不是那麼簡單,但Session原理不是本文介紹重點,是以請大家自行度娘。

Session分布式共享 = Session + Redis + Nginx

2、又愛又恨的Session

      剛接觸程式開發的人一定愛死Session了,因為Session讓Http從無狀态變成有狀态了,頁面之間傳值、使用者相關資訊、一些不變的資料、甚至于查出來的DataTable也可以放進去,取值的時候隻需要Session[Key]即可,真是友善極了。Session真是個利器,人擋殺人佛擋殺佛,但任何事物被封為利器基本也是雙刃劍,Session的許多問題我們不得不去面對。

【常見問題請見下圖】

Session分布式共享 = Session + Redis + Nginx

      我相信一見到這個問題,老程式員都會心裡一哆嗦,Session是導緻這個原因之一,大家也會想到這個情景,“我去,是不是Session又丢了,讓使用者重新登入”,事故報告中會填寫:.NET規定,使用者登陸後長時間沒操作導緻的。解決方案為:把Session時間調到9999。

      結果該發生的還是繼續發生着,Session照樣丢失。

【常見Session丢失原因】

      1、Session逾時,使用者打開頁面,頁面長時間不操作會導緻此原因

      2、IIS應用程式池回收,或者重新開機

      3、Web.Config修改,即IIS應用程式池重新開機

      4、dll被替換或者動态頁面修改,即IIS應用程式池重新開機

      5、防毒軟體對.config檔案進行掃描,可能會導緻IIS應用程式池回收

      6、使用者浏覽器禁用cookie

      7、其他原因

      其他原因有點不負責,但是好多程式員無法查明是什麼原因導緻Session丢失,但Session丢失我歸結為兩大類,一個是資料的Key丢了,一個是Session内容資料庫的丢了,大家這樣就好了解了,使用者浏覽器禁用cookie一定是Key沒了。IIS應用程式池回收必定會導緻Session的内容緩存表丢失,當然還有一些其他原因。

3、解決Session丢失的漫長路

      解決過Session丢失的都會用到這幾種方法

          1、InProc:将Session存到程序内。

          2、StateServer:将Session存到獨立的狀态服務中(Asp.Net State Service)。

          3、SqlServer:将Session存到SqlServer中。

          4、Cookieless:設定用戶端Session存儲的方式。

     用了這些方法之後,有的是該丢還丢,有的是穩定了速度卻慢了。

     大家也注意到了,還有個這個Custom自定義模式,有人會說:“除了大牛,有幾個敢寫的啊,寫出來有問題怎麼辦,算了算了。” 等等,大家不要還停留在非開源模式下解決問題的思想,找找開源項目,一定能找到的,有人說ASP.NET上哪裡找開源啊,非常簡單NuGet,如果想了解開源,一定要學會使用NuGet。

二、Redis

1、前言  

     上文說了那麼多,有人一定會說我是來解決Session丢失的,上哪裡來的Session分布式共享,标題黨,我還是繼續用我的cookie吧。

     我要說的是,幾年前,在

Stack Overflow

上找到了這個方法解決了丢失問題,之後,發現這種方法還可以實作Session分布式共享。那就是運用Custom自定義模式,将Session持久化到Memcache和Redis中。Session丢失、以及持久化到SqlServer資料的性能問題也随之解決。

     此種方法很适合老項目中大量應用Session而導緻法搞成分布式而苦惱的.NET開發人員使用。因為很有可能老項目維護過程中,身邊的JAVA團隊、PHP團隊,正在重構你的項目。

2、RedisSessionProvider

     正文開始,首先,沿着我們的思路Session持久化到Memcache或者Redis中,通過nuget下載下傳 RedisSessionProvider(别問我怎麼找到的,因為我英文過了四級,我會使用度娘,嘿嘿)

Session分布式共享 = Session + Redis + Nginx
【web.config配置如下】

<system.web>
    <sessionState mode="Custom" customProvider="RedisSessionProvider">
      <providers>
        <add name="RedisSessionProvider" type="RedisSessionProvider.RedisSessionStateStoreProvider, RedisSessionProvider"/>
      </providers>
    </sessionState>
</system.web>      

【Global.asax】

void Application_Start(object sender, EventArgs e)
    {
       
        StackExchange.Redis.ConfigurationOptions redisConfigOpts = StackExchange.Redis.ConfigurationOptions.Parse("192.168.8.138:6379");
        RedisSessionProvider.Config.RedisConnectionConfig.GetSERedisServerConfig = (HttpContextBase context) =>
        {
            return new KeyValuePair<string, StackExchange.Redis.ConfigurationOptions>(
                "DefaultConnection",                
                redisConfigOpts);
        };

    }      

【存儲方法】

Session["Test"] = "aa";      

【調用方法】

string str = Session["Test"].ToString()      

 !前方坑,請注意!

    如果你配置好Redis,并且做好上面這些配置,運作會出現以下問題,請更新RedisSessionProvider的依賴包StackExchange.Redis到最新。

Session分布式共享 = Session + Redis + Nginx
Session分布式共享 = Session + Redis + Nginx

3、Redis安裝

3-1、Redis for windows下載下傳

     如果會配置Redis的同學,請略過此章節,直接進入Nginx。

     Redis下載下傳:

https://github.com/MSOpenTech/redis
Session分布式共享 = Session + Redis + Nginx

!此處為坑,請注意!

     修改Redis.windows.conf,如果不修改,遠端不能通路Redis

     1、将bind 127.0.0.1 改成了bind 0.0.0.0。注意:進入生産環境時候,要啟用密碼,否則會是Redis漏洞,具體請自行度娘和自己公司的運維阿牛

     2、protected-mode yes 改成 protected-mode no

     詳細修改的傳送門: 

redis開啟遠端通路

3-2、啟動Redis

redis-server redis.windows.conf      
Session分布式共享 = Session + Redis + Nginx

     上圖為redis啟動成功,預設6379,可以通過redis-cli進行測試,看别的機子能否通路。還可以在找個redis可視化工具看看裡面存了啥,也可以監控Session是否持久化到Redis中了。

3-3、驗證Session是否持久化到Redis

     運作RedisSessionProvider這個項目。同一個IIS下,同域名,不同IP,同一浏覽器,不同端口一個是2459,一個是2490。

Session分布式共享 = Session + Redis + Nginx

【注意】

     不同浏覽器SessionId是不同的。必須保證SessionId,測試必須是同一個浏覽器程序分出的不同子标簽才可以,這樣SessionId是共享的。

Session分布式共享 = Session + Redis + Nginx

     感覺成功了,讓我們看看這樣的拓撲圖:

Session分布式共享 = Session + Redis + Nginx

     囧……這是啥玩意?我的分布式呢?這個拓撲圖很顯然不是分布式啊,還兩個IP,我還要在前面做個路由登入頁面?這時Nginx該登場了。

三、Ngnix 

1、Ngnix安裝&下載下傳

      下載下傳位址:

http://nginx.org/

2、nginx.conf配置修改

Session分布式共享 = Session + Redis + Nginx

2-1、【接口修改】

       listen   80; 改成  listen   1100; 因為一般都被80都被使用。

Session分布式共享 = Session + Redis + Nginx

2-2、【增加負載均衡】

upstream  Jq_one {  

     server 127.0.0.1:8770;
     server 192.168.8.138:7777;
} 
server {
.....
}      
Session分布式共享 = Session + Redis + Nginx

2-3、【location節點修改】

location / {
            root   html;
            index  index.aspx index.html index.htm;
            #其中jq_one 對應着upstream設定的叢集名稱
            proxy_pass         http://Jq_one; 
            #設定主機頭和用戶端真實位址,以便伺服器擷取用戶端真實IP
            proxy_set_header   Host             $host; 
            proxy_set_header   X-Real-IP        $remote_addr; 
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }      

2-4、【Nginx啟動指令】

        C:\server\nginx-1.0.2>start nginx

        或

        C:\server\nginx-1.0.2>nginx.exe

2-5、【Nginx重新載入指令】

        C:\server\nginx-1.0.2>nginx.exe -s reload

2-6、【參考文章】

         詳細配置傳送門:

nginx+iis實作負載均衡

四、Session分布式共享

1、拓撲圖

Session分布式共享 = Session + Redis + Nginx

         通過Nginx+Redis實作對Session的分布式共享功能。通過測試,發現Session分布式共享共有兩種解決方案。

2、利用Nginx的Ip_Hash進行Session分布式共享

         使用nginx将同一ip的請求配置設定到固定伺服器,修改如下。ip_hash會計算ip對應hash值,然後配置設定到固定伺服器

upstream Jq_one{
     server 127.0.0.1:8770;

     server 192.168.8.138:7777;      
  ip_hash;      
}      

         效果可以了解為就是一個Ip,通過Nginx路由到IIS_1上面,在多次請求,會一直在IIS_1上,不會路由到IIS_2上面。

Session分布式共享 = Session + Redis + Nginx

3、利用MachineKey進行Session分布式共享

     Ip_Hash在一定程度上解決了Session分布式共享的問題,但是總感覺沒有發揮出nginx均衡負載的功能,繼續改造

3-1、現将Ip_Hash去掉

    去掉Ip_Hash重新開機Nginx,打開網站,點選設定Session按鈕,結果報錯

Session分布式共享 = Session + Redis + Nginx

3-2、web.config添加MachineKey

<machineKey
   validationKey="86B6275BA31D3D713E41388692FCA68F7D20269411345AA1C17A7386DACC9C46E7CE5F97F556F3CF0A07159659E2706B77731779D2DA4B53BC47BFFD4FD48A54"
   decryptionKey="9421E53E196BB56DB11B9C25197A2AD470638EFBC604AC74CD29DBBCF79D6046"
   validation="SHA1"
   decryption="AES"
/>      

     負載均衡的兩個網站的MachineKey必須一樣,否則出問題。

3-3、示範

     下圖,大家可以看到,伺服器的Ip在不斷變化,而Session卻沒有丢失,至此實作了Session分布式共享。

Session分布式共享 = Session + Redis + Nginx

五、後記&感悟

   希望能通過本文,解決有的項目中Session分布式共享和Session丢失的難題,給大家一些解決問題、分析問題啟發。

     ASP.Net給我們帶來了新的一種編碼體驗,如今.Net已經15歲了,.Net的在企業中發展中扮演最多的角色是快槍手和背鍋俠的角色,在企業剛起步時候選擇易上手的.Net無非是最好的選擇之一,但是因為.Net的高度封裝,讓.Net進階人才在市場上十分稀少,而且企業在創立之初應用.net的時候也不會考慮架構之類的問題。可是随着業務越來越複雜,.Net開發人員無法解決和滿足市場的需求和項目中出現的技術難題,技術債随之産生,解決不了問題随之一些程式員便讓.Net背鍋,再加上.Net的新技術推陳出新(有好多人說微軟瞎折騰,囧),WebForm、mvc、silverlight、sharepoint、wpf、window phone、wcf等等。可是中國大環境并不買賬,慢慢成長的企業發現.Net現有架構無法滿足,.Net開發人員又無法解決現有問題,又找不到.Net架構師之類的角色,又看到了京東等大廠轉JAVA的成功,其他企業家便會想咱也轉個JAVA試試,招聘Java架構師,結果一大堆人應聘(進階的、中級的、技術總監),企業家高興壞了,很快JAVA便組建了一支精英團隊準備重構.net項目。JAVA發展的曆史比Net要長很多,早些做JAVA開發早已轉型為管理,你會發現現在市場上,挖過來的其中一些CTO很喜歡組建JAVA、PHP團隊,NET團隊很少,原因多數是.Net不開源、不跨平台、解決方案不成熟,背後的原因就不得而知了。但不得不說,JAVA語言很容易培養牛人,因為當你學了JAVA中的Spring,你就開始接觸了IOC容器,你就在慢慢的面向接口程式設計,當你學會了的AOP,你就開始在面向方面程式設計的道路上邁出了一小步。語言隻是一個工具,幹了這麼多年.Net,看看招聘資訊或多或少有了些迷茫,路是自己走的,做出的決定就不能後悔,但在十字路口時,還是多想一想。

     以上為個人觀點,有可能因為知識和閱曆的原因,分析片面,請多諒解。

六、參考文章

ASP.NET性能優化之分布式Session .Net分布式架構(一):Nginx實作負載均衡 .Net分布式架構(二):基于Redis的Session共享

非常感謝上述文章,對本文的啟發,謝謝。