nginx+iis+redis+Task.MainForm構建分散式架構 之 (redis存儲分散式共用的session及共用session運作流程)

来源:http://www.cnblogs.com/wangrudong003/archive/2016/09/28/5917602.html
-Advertisement-
Play Games

本次要分享的是利用windows+nginx+iis+redis+Task.MainForm組建分散式架構,上一篇分享文章製作是在windows上使用的nginx,一般正式發佈的時候是在linux來配置nginx,我這裡測試分享內容只是起引導作用;下麵將先給出整個架構的核心節點簡介,希望各位多多點贊 ...


本次要分享的是利用windows+nginx+iis+redis+Task.MainForm組建分散式架構,上一篇分享文章製作是在windows上使用的nginx,一般正式發佈的時候是在linux來配置nginx,我這裡測試分享內容只是起引導作用;下麵將先給出整個架構的核心節點簡介,希望各位多多點贊:

 

. 架構設計圖展示

nginx+iis構建服務集群

. redis存儲分散式共用的session及共用session運作流程

. redis主從配置及Sentinel管理多個Redis集群

. 定時框架Task.MainForm提供數據給redis集群儲存

 

以上是整個架構的我認為核心的部分,其中沒有包含有資料庫方面的設計(請忽略),下麵來正式分享今天的文章吧(redis存儲分散式共用的session及共用session運作流程):

. 理解分散式的session(個人理解)

. 分析分散式session的流轉過程

. 封裝session登錄驗證和退出的公共方法

. Redis存儲分散式session的登錄實例

 

下麵一步一個腳印的來分享:

. 理解分散式的session(個人理解)

首先,操作session方式,這裡要說的是登錄的session,理解是基於個人觀點來的,並且這裡的理解可能不是那麼深刻哈;通常我們建設的網站或者管理系統都有用戶登陸,登陸後會有一個存儲用戶基本信息的session保存在伺服器,保存session的方法有多中,這裡要分享的是使用redis來存儲session;隨著登陸用戶增多,存儲在伺服器上的session也越來越多,如果某台伺服器上使用記憶體保存的sesssion那伺服器的記憶體占有會對比的提升,最終可能直接奔潰,嚴重的由於長時間100%記憶體占用率可能導致硬碟燒壞,由此產生了多種存儲方式如:使用同步session到不同伺服器方式做讀寫分離,資料庫存儲session等方式;其實我們要用到的redis集群存儲操作session的方式也主要是分攤讀寫;

其次,session的讀寫分離通常都對應站點有很大的訪問量了,如果訪問量如此之大那麼站點的發佈對應的應該也是集群的方式(名為分散式架構),分散式架構和單站點模式對比最明顯的在於分散式對應的多個站點只需要使用其中某一個登陸入口登陸後,其他站點共用此session,無需再做登陸操作,其實這裡就可以看做是單點登陸,只是分散式集群一般訪問的都是同一個功能變數名稱或同一ip段而已;而單站點模式通常就是我這裡登陸了,就只能我本系統能使用,另外的系統無法使用(常規來講);

最後,既然要滿足共用session,那麼session要麼就是保存在同一個地方,讀取的時候也在同一個地方讀取;要麼就redis集群這種方式實現即時同步到不同伺服器上或不同埠實現數據讀寫分離;這樣就能保證統一數據源;

 

. 分析分散式session的流轉過程

首先,上面的內容也基本介紹了下分散式session(session數據源統一),這裡要說的是分散式登錄時幾個疑問:

. 系統怎麼產生共用session

. 用戶根據何種數據取得相同的session

. 共用session生存的周期

下麵來給出對應上面問題的回答及說明:

. 產生session其實就是保存session數據,在用戶使用分散式站點第一次登陸時,從資料庫檢查此賬號運行登陸,在返回登錄成功信息給用戶前,會先生成一個分散式系統中唯一的一個key,這個key通常使用的規則是分散式站點Id(每個分散式子站點對應的Id)+時間戳+用戶登陸唯一的賬號+加密串+Guid組合而成(可能有其他不同的保證唯一key的方法吧),然後用md5或hash等加密,再把用戶的基礎信息和key一起保存到指定的Redis服務(姑且用redis存session,通常是鍵值對的關係)中,並且會返回key到用戶的cookie中

. 用戶要去的對應的session,就是通過cookie存儲的key傳遞給每台分散式的站點,站點獲取cookie再去指定的session讀取的地方獲取是否有對應的key並獲取session保存的數據;只要用戶有有效的cookie就能登錄分散式系統;假如我用ie瀏覽器登陸系統後,再使用google瀏覽器訪問系統,這樣使用google瀏覽器時候登陸不成功的,因為cookie沒有跨域,但是如果您手動或者通過其他人為方式利用ie登陸成功後返回的cookie的key加入到google中,那麼同樣登陸也是沒問題的,可以試試按照原理分析是沒問題的

. session的生命周期大家應該都很關註,通常一個session不可能設置成無線久的生命周期,這個時候就需要按照每次用戶觸發驗證登陸的時候,自動從新設置session失效時間(一般就當前時間往後推您session定義的過期時間);由於分散式用到了cookie所以此時還需要重新更新設置下cookie的key過期時間,這樣使用cookie+seesion來保存用戶的登陸有效性,直到用戶超過了session有效期還沒有觸發過登陸驗證或者特殊方法清除了cookie,那這個時候過期的cookie或session就會驗證用戶需要登錄才能訪問需要許可權的頁面

 

. 封裝session登錄驗證和退出的公共方法

首先,將要發出來的兩個C#方法都進過測試了,大家可以直接拿來使用,當然此方法用到了前面分享的CacheRepository緩存工廠,因為我保存session是在redis中,下麵先來貼出方法內容:

 1  /// <summary>
 2     /// Login擴展類
 3     /// </summary>
 4     public class UserLoginExtend
 5     {
 6 
 7         public static string HashSessionKey = "Hash_SessionIds";
 8         public static string CookieName = "Sid";
 9 
10         public static T BaseSession<T>(HttpContextBase context) where T : class,new()
11         {
12             //獲取cookie中的token
13             var cookie = context.Request.Cookies.Get(CookieName);
14             if (cookie == null) { return default(T); }
15 
16             //使用toke去查詢緩存工廠是否有對應的session信息,如果有自動把緩存工廠的時間往後延nAddCookieExpires分鐘
17             //return CacheRepository.Current(CacheType.RedisCache).GetHashValue<T>(HashSessionKey, cookie.Value);
18             return CacheRepository.Current(CacheType.RedisCache).GetCache<T>(cookie.Value);
19         }
20 
21         public static RedirectResult BaseCheckLogin<T>(
22             HttpContextBase context,
23             out T t,
24             int nAddCookieExpires = 30,
25             string loginUrl = "/User/Login") where T : class,new()
26         {
27             var returnUrl = context.Request.Path;
28             var result = new RedirectResult(string.Format("{0}?returnUrl={1}", loginUrl, returnUrl));
29             t = default(T);
30             try
31             {
32 
33                 //獲取cookie中的token
34                 var cookie = context.Request.Cookies.Get(CookieName);
35                 if (cookie == null) { return result; }
36 
37                 //使用toke去查詢緩存工廠是否有對應的session信息,如果有自動把緩存工廠的時間往後延nAddCookieExpires分鐘
38                 //t = CacheRepository.Current(CacheType.RedisCache).GetHashValue<T>(HashSessionKey, cookie.Value);
39                 t = CacheRepository.Current(CacheType.RedisCache).GetCache<T>(cookie.Value, true);
40                 if (t == null)
41                 {
42                     //清空cookie
43                     cookie.Expires = DateTime.Now.AddDays(-1);
44                     context.Response.SetCookie(cookie);
45                     return result;
46                 }
47 
48                 //登陸驗證都成功後,需要重新設置cookie中的toke失效時間
49                 cookie.Expires = DateTime.Now.AddMinutes(nAddCookieExpires);
50                 context.Response.SetCookie(cookie);
51 
52                 //設置session失效時間
53                 CacheRepository.Current(CacheType.RedisCache).AddExpire(cookie.Value, nAddCookieExpires);
54             }
55             catch (Exception ex)
56             {
57                 return result;
58             }
59             return null;
60         }
61 
62         public static RedirectResult BaseLoginOut(HttpContextBase context, string redirectUrl = "/")
63         {
64             var result = new RedirectResult(string.IsNullOrEmpty(redirectUrl) ? "/" : redirectUrl);
65             try
66             {
67                 //獲取cookie中的token
68                 var cookie = context.Request.Cookies.Get(CookieName);
69                 if (cookie == null) { return result; }
70 
71                 var key = cookie.Value;
72 
73                 //設置過期cookie(先過期cookie)
74                 cookie.Expires = DateTime.Now.AddDays(-1);
75                 context.Response.SetCookie(cookie);
76 
77                 //移除session
78                 //var isRemove = CacheRepository.Current(CacheType.RedisCache).RemoveHashByKey(HashSessionKey, key);
79                 var isRemove = CacheRepository.Current(CacheType.RedisCache).Remove(key);
80             }
81             catch (Exception ex)
82             {
83 
84                 throw new Exception(ex.Message);
85             }
86             //跳轉到指定地址
87             return result;
88         }
89     }

BaseCheckLogin方法主要用來驗證是否登錄,沒有登錄跳轉到重定向地址中;如果驗證是登錄狀態,會自動重新設置redis存儲的session有效期,並重新設置cookie有效期;看代碼的話其實就那點重要的地方都有備註說明;

BaseLoginOut方法主要用來清空用戶註銷後的session數據和cookie數據;這兩個方法都是通常登陸驗證需要的內容,兩方法返回的是RedirectResult,適用於.net的mvc版本;

 

. Redis存儲分散式session的登錄實例(這裡是.net mvc代碼操作)

首先,看下登陸的action代碼:

 1 [HttpPost]
 2         //[ValidateAntiForgeryToken]
 3         public ActionResult Login([Bind(Include = "UserName,UserPwd", Exclude = "Email")]MoUserInfo model, string returnUrl)
 4         {
 5 
 6             if (ModelState.IsValid)
 7             {
 8                 //初始化資料庫讀取數據  nginx+iis+redis+Task.MainForm組建分散式架構 - (nginx+iis構建服務集群)
 9                 model.Email = "[email protected]";
10                 model.Id = 1;
11                 model.Introduce = "專註web開發二十年";
12                 model.Sex = false;
13                 model.Tel = "183012787xx";
14                 model.Photo = "/Content/ace-master/assets/images/avatars/profile-pic.jpg";
15 
16                 model.NickName = "神牛步行3";
17                 model.Addr = "北京-亦莊";
18                 model.Birthday = "1991-05-31";
19                 model.Blog = "http://www.cnblogs.com/wangrudong003/";
20 
21                 var role = new StageModel.MoRole();
22                 role.Name = "系統管理員";
23                 role.Des = "管理整個系統";
24 
25                 //根據角色Id獲取對應菜單Id,這裡構造成List<int>形式
26                 var menus = new List<StageModel.MoMenu>{
27                   new  StageModel.MoMenu{
28                       Id = 1001,
29                       Link="/User/UserCenter"
30                   },
31                    new  StageModel.MoMenu{
32                       Id = 1002,
33                       Link="/User/ChangeUser1"
34                   },
35                   new  StageModel.MoMenu{
36                       Id = 1003,
37                       Link=""
38                   },
39 
40                    new  StageModel.MoMenu{
41                       Id = 2001001,
42                       Link=""
43                   },
44                    new  StageModel.MoMenu{
45                       Id = 2001002,
46                       Link=""
47                   }
48                 };
49 
50                 //賦值個人信息
51                 var userData = new StageModel.MoUserData();
52                 userData.Email = model.Email;
53                 userData.Id = model.Id;
54                 userData.Introduce = model.Introduce;
55                 userData.Sex = model.Sex;
56                 userData.Tel = model.Tel;
57                 userData.Photo = model.Photo;
58 
59                 userData.UserName = model.UserName;
60                 userData.NickName = model.NickName;
61                 userData.Addr = model.Addr;
62                 userData.Birthday = model.Birthday;
63                 userData.Blog = model.Blog;
64 
65                 //能訪問菜單的Ids  
66                 userData.Menus = menus;
67 
68                 //獲取唯一token
69                 var token = CacheRepository.Current(CacheType.BaseCache).GetSessionId(userData.UserName);
70                 var timeOut = 2; //分鐘
71                 //if (CacheRepository.Current(CacheType.RedisCache).SetHashCache<StageModel.MoUserData>("Hash_SessionIds", token, userData,2))
72                 if (CacheRepository.Current(CacheType.RedisCache).SetCache<StageModel.MoUserData>(token, userData, 2, true))
73                 {
74                     var cookie = new HttpCookie(UserLoginExtend.CookieName, token);
75                     cookie.Expires = DateTime.Now.AddMinutes(timeOut);
76                     HttpContext.Response.AppendCookie(cookie);
77 
78                     return new RedirectResult(returnUrl);
79                 }
80             }
81 
82             return View(model);
83         }
View Code

裡面用到了 var token = CacheRepository.Current(CacheType.BaseCache).GetSessionId(userData.UserName)  方法,這個方法主要是用來獲取上面說的分散式唯一的key,參數只需要傳遞用戶登陸的唯一賬號就行了(底層用的是Md5hash值演算法,文字結尾給出所以代碼);獲取key後使用 CacheRepository.Current(CacheType.RedisCache).SetCache<StageModel.MoUserData>(token, userData, 2, true) 方法來設置登陸的基本信息到redis服務中,如果保存redis數據成功再通過 HttpContext.Response.AppendCookie(cookie); 吧key輸出到用戶的cookie中保存;

然後,登陸後通常會跳轉到用戶後臺,用戶後臺的一些頁面需要登錄驗證,我這裡是使用後臺幾個Controller來繼承同一個父級BaseController,父級裡面重寫Initialize方法來驗證登陸信息;代碼如下:

 1 public class BaseController : Controller
 2     {
 3 
 4         protected StageModel.MoUserData userData;
 5 
 6         protected override void Initialize(System.Web.Routing.RequestContext requestContext)
 7         {
 8 
 9             //使用登錄擴展,驗證登陸,獲取登陸信息
10             var redirectResult = UserLoginExtend.BaseCheckLogin(requestContext.HttpContext, out userData,2);
11             //驗證失敗,跳轉到loginUrl
12             if (redirectResult != null)
13             {
14                 requestContext.HttpContext.Response.Redirect(redirectResult.Url, true);
15                 return;
16             }
17 
18             //驗證成功,添加視圖訪問登陸信息數據
19             ViewBag.UserData = userData;
20             base.Initialize(requestContext);
21         }
22     }

BaseCheckLogin方法就是我們上面分享的公共驗證登陸的方法,具體參數可以看下參數描述說明;代碼寫好後,來看下運行的頁面效果(我這裡使用的是前一章大家的nginx集群來演示):

紅色框裡面的就是咋們自己生產的Sid也就是上面說的key,接著咋們在打開一個瀏覽器tab,來看下系統02的Sid,如圖:

通過上圖可以看到系統01和系統02,對應的sid都是一樣的值,每次這樣分散式站點的session使用和製作就成功了,好那咋們通過redis-cli.exe客戶端看下我們登陸後保存在redis服務中的數據圖如:

看到的redis里的key和我們瀏覽器截圖中的key是一樣的,所以本章要將的內容大致就要結束了,如果覺得文章讓您有所收穫,請多多點"",謝謝。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Ubuntu keylin 14.04 如何開啟root用戶登錄 Ubuntu keylin 14.04 如何開啟root用戶登錄 Ubuntu(烏班圖)是一個以桌面應用為主的Linux操作系統。Ubuntu基於Debian發行版和GNOME桌面環境,而從11.04版起,Ubuntu發行版放棄了Gn ...
  • 練習一: 1、添加5個用戶,user1,...,user5; 2、每個用戶的密碼同用戶名,添加密碼完成後,不顯示命令的執行結果; 3、每個用戶添加完成後,都要顯示用戶某某已添加成功。 useradd user1 echo "user1" | passwd --stdin user1 &> /dev/ ...
  • 古語云,“工欲善其事,比先利其器 ”,每個行業的從業者,都有一套特定的工具以幫助他們更快速的解決問題,木匠需要鋸子、卷尺、刨子、錘子,廚師需要一套刀,剁、切、片、剔,一套勺子,炒、煎、燉、滷;就連路邊擺攤貼膜的小伙,也有一整套的的工具,剪刀、鋼尺、擦布、推膜卡;對於一個追求效率的碼農,也必須要一套工 ...
  • 本文記錄的是自己在學習《Linux私房菜》中正則表達式的筆記。 關於行尾符$ 如果文件本身沒有內容,比如使用touch新建的文件,那麼$將會沒有意義。例如下麵操作: 先使用touch新建了一個test.txt文件,然後嘗試使用sed命令去在行尾增加內容,但是沒有成功(緊接著的cat命令沒有輸出任何東 ...
  • 今天分享一個linux命令:mv 1. 命令作用 a. 移動文件 b. 修改文件或者目錄的名稱 2. 舉例說明 場景:/home/user1/test1下麵有一個文件a.txt、b.txt, test2文件夾與test1文件夾同在/home/user1下麵 移動文件: 將a.txt移動到test2下 ...
  • 橋接:區域網/路由器內獲取IP,IP地址發生變化, NAT方式或者僅主機模式(Host-only):把本機電腦當做轉換機去獲取IP,不能禁止使用,IP不會變換, 網路 屬性 更改適配器設置 VMnet1-->Hostonly(不能上外網) VMnet8-->NAT ...
  • 接觸C#開發已經四個月,整理下C#中的命名規則: 一:變數的命名規則(和Java相似) 1、變數名由字母、數字、下劃線組成 2、變數名開頭只能以字母、下劃線開頭,不能以數字開頭 3、區分大小寫 4、命名規範:一般遵循camel(駱駝命名法)——首字母小寫第二個單詞的首字母大寫例如myName 二:類 ...
  • 本文主要是對unity中如何在Android和iOS中調用Native API進行介紹。 首先unity支持在C#中調用C++ dll,這樣可以在Android和iOS中提供C++介面在unity中調用。利用這一特性,可以擴展unity的功能。例如集成和調用第三方庫。同時為了滿足對unity介面的一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...