一、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原理不是本文介紹重點,所以請大家自行度娘。
2、又愛又恨的Session
剛接觸程序開發的人一定愛死Session了,因為Session讓Http從無狀態變成有狀態了,頁面之間傳值、用戶相關信息、一些不變的數據、甚至於查出來的DataTable也可以放進去,取值的時候只需要Session[Key]即可,真是方便極了。Session真是個利器,人擋殺人佛擋殺佛,但任何事物被封為利器基本也是雙刃劍,Session的許多問題我們不得不去面對。
【常見問題請見下圖】
我相信一見到這個問題,老程序員都會心里一哆嗦,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(別問我怎么找到的,因為我英文過了四級,我會使用度娘,嘿嘿)
【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到最新。
3、Redis安裝
3-1、Redis for windows下載
如果會配置Redis的同學,請略過此章節,直接進入Nginx。
Redis下載:https://github.com/MSOpenTech/redis
!此處為坑,請注意!
修改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
上圖為redis啟動成功,默認6379,可以通過redis-cli進行測試,看別的機子能否訪問。還可以在找個redis可視化工具看看里面存了啥,也可以監控Session是否持久化到Redis中了。
3-3、驗證Session是否持久化到Redis
運行RedisSessionProvider這個項目。同一個IIS下,同域名,不同IP,同一瀏覽器,不同端口一個是2459,一個是2490。
【注意】
不同瀏覽器SessionId是不同的。必須保證SessionId,測試必須是同一個瀏覽器進程分出的不同子標簽才可以,這樣SessionId是共享的。
感覺成功了,讓我們看看這樣的拓撲圖:
囧……這是啥玩意?我的分布式呢?這個拓撲圖很顯然不是分布式啊,還兩個IP,我還要在前面做個路由登錄頁面?這時Nginx該登場了。
三、Ngnix
1、Ngnix安裝&下載
下載地址:http://nginx.org/
2、nginx.conf配置修改
2-1、【接口修改】
listen 80; 改成 listen 1100; 因為一般都被80都被使用。
2-2、【增加負載均衡】
upstream Jq_one { server 127.0.0.1:8770; server 192.168.8.138:7777; } server { ..... }
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、拓撲圖
通過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上面。
3、利用MachineKey進行Session分布式共享
Ip_Hash在一定程度上解決了Session分布式共享的問題,但是總感覺沒有發揮出nginx均衡負載的功能,繼續改造
3-1、現將Ip_Hash去掉
去掉Ip_Hash重啟Nginx,打開網站,點擊設置Session按鈕,結果報錯
3-2、web.config添加MachineKey
<machineKey validationKey="86B6275BA31D3D713E41388692FCA68F7D20269411345AA1C17A7386DACC9C46E7CE5F97F556F3CF0A07159659E2706B77731779D2DA4B53BC47BFFD4FD48A54" decryptionKey="9421E53E196BB56DB11B9C25197A2AD470638EFBC604AC74CD29DBBCF79D6046" validation="SHA1" decryption="AES" />
【注意】
負載均衡的兩個網站的MachineKey必須一樣,否則出問題。
3-3、演示
下圖,大家可以看到,服務器的Ip在不斷變化,而Session卻沒有丟失,至此實現了Session分布式共享。
五、參考文章
.Net分布式架構(二):基於Redis的Session共享
非常感謝上述文章,對本文的啟發,謝謝。