.Net Core Web Api實踐(三).net core+Redis+docker實現Session共享


前言:上篇文章介紹了.net core+Redis+IIS+nginx實現Session共享,本來打算直接說明后續填坑過程,但畢竟好多坑是用docker部署后出現的,原計划簡單提一下.net core+Redis+docker實現Session共享,但是發現篇幅也不小,所以還是單獨起草一篇,除了k8s部署docker,其它部分都有基本介紹。

1、環境准備

操作系統:Windows10

VS2019、本地Redis數據庫、Windows docker

2、背景介紹

由於項目從asp.net MVC向.net core webapi遷移,一方面是技術方面的遷移,另一方面是從業務方面切割,向微服務模式轉型,項目最后完成部署的結構大致如下:

 

 

 總體上說,大家各自的項目有各自的部署方式,一旦做成分布式的,實現Session共享往往就不可避免了。

 

3、.net core+Redis+docker實現Session共享

如果你的項目是用IIS或其它方式部署,那么這部分你可以直接跳過了,因為代碼部分跟上篇文章是一樣的。無非是使用windows docker 命令進行部署。

(1)用VS2019新建一個Web Api項目(RedisSessionTest)

在Startup.cs文件中添加以下代碼  

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => false; //這里要改為false,默認是true,true的時候session無效
                options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None;
            });

            services.AddDataProtection(configure =>
            {
                configure.ApplicationDiscriminator = "commonweb1";
            })
            .SetApplicationName("commonweb1")
            .AddKeyManagementOptions(options =>
            {
                //配置自定義XmlRepository
                options.XmlRepository = new SessionShare();
            });

            //services.AddSession();
            #region 使用Redis保存Session
            // 這里取連接字符串 
            services.AddDistributedRedisCache(option =>
            {
                //redis 連接字符串
                option.Configuration = "";
                //redis 實例名
                option.InstanceName = "Test_Session";
            });

            //添加session 設置過期時長分鍾  
            //var sessionOutTime = con.ConnectionConfig.ConnectionRedis.SessionTimeOut;
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromSeconds(Convert.ToDouble(3 * 60 * 60)); //session活期時間
                options.Cookie.HttpOnly = true;//設為httponly
            });
            #endregion

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }


        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseSession();
            app.UseMiddleware<RequestMiddleware>();            
            app.UseCookiePolicy();
            app.UseMvc();
        }

  

為什么這么加請參考上篇文章,這里我只做一個簡單的介紹:

services.Configure<CookiePolicyOptions>:配置可以讀取cookie信息。

app.UseCookiePolicy():表示使用ConfigureServices中配置cookie策略

services.AddDataProtection:配置應用程序名稱,自定義MachineKey,用於不同站點服務可以讀取同一Session。

services.AddDistributedRedisCache:將Session保存到Redis數據庫。

services.AddSession:配置Sesion策略。

app.UseMiddleware<RequestMiddleware>():使用自定義中間件。

(2)添加自定義中間件RequestMiddleware

public class RequestMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestMiddleware(RequestDelegate next)
        {
            this._next = next;
        }

        public Task Invoke(HttpContext context)
        {
            context.Request.EnableRewind(); //支持context.Request.Body重復讀取,內部調用了EnableBuffering方法,否則在使用部分方法或屬性時會報錯誤System.NotSupportedException: Specified method is not supported,例如context.Request.Body.Position
            if (context.Request.ContentLength == null)
            {
                return this._next(context);
            }

            string sessionPhone = context.Session.GetString("phone");
            if (string.IsNullOrEmpty(sessionPhone))
            {
                context.Session.SetString("phone", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            }
            return this._next(context);
        }
    }

  在中間件中保存當前時間到Session中。

(3)在ValuesController中添加測試接口

[HttpPost]
        public string PostTest(dynamic @param)
        {            
            string phone = HttpContext.Session.GetString("phone");
            return JsonConvert.SerializeObject(@param) + phone;
        }

  為了方便我把路由從api/[Controller]改成了[action]:

 

 (4)添加dockerfile文件如下(如果用別的方式部署,后續步驟可直接跳過,如果想了解windows docker的安裝和部署,可以點擊;如果想深入了解docker,這里我也幫不了多少,自己還在進一步學習中):

 

 (5)使用docker命令(windows版)部署測試項目

打開cmd命令,cd定位到項目路徑

 

 

生成鏡像(最后面的.不能去掉): docker build -f /Redis使用測試/RedisSessionTest/RedisSessionTest/Dockerfile -t testcore .

映射容器端口:docker run --name testweb -p 7001:80 -d testcore

利用fiddler模擬請求,調用步驟3中創建的PostTest接口,驗證是否部署成功:

點擊composer->輸入接口地址->設置contentype頭信息->添加參數為{"qqq":147},最后得到結果是: {"qqq":147}2020-01-13 09:16:58

特別留意下這個cookie信息,它將作為另外一個站點下,同http://xxxx:7001/PostTest接口共享Session的接口的請求頭信息。

 

 

 

 可以發現Session緩存的時間是2020-01-13 09:16:58,這里注意一下,docker容器所在linux系統中的時間比windows當前時間早了8個小時,也就是說我實際做測試的時間是2020-01-13 17:16:58,如果要解決這個問題,在dockerfile文件中加入時區設置:

#設置時區
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

(6)重復上面幾個步驟,新增一個新的web api項目(RedisSessionTestNew)

在第(3)步的時候,將新增項目的接口action改為PostTestNew,用於區別RedisSessionTest項目,即代碼如下:

[HttpPost]
        public string PostTestNew(dynamic @param)
        {            
            string phone = HttpContext.Session.GetString("phone");
            return JsonConvert.SerializeObject(@param) + phone;
        }

  

在第(5)步的時候,將新的項目映射為7002端口,我的測試項目部署如下:

生成鏡像(最后面的.不能去掉): docker build -f /Redis使用測試/RedisSessionTest/RedisSessionTestNew/Dockerfile -t testnewcore .

映射容器端口:docker run --name testnewweb -p 7002:80 -d testnewcore

 

接下來再使用fiddler去調用7002站點下的PostTestNew接口,注意帶上7001PostTest測試結果中的cookie信息,參數為{"qqq":258},結果如下:{"qqq":258}2020-01-13 09:16:58

 

 

 

 

 

 

4、分析測試結果

這里對比下兩次請求結果:

http://XXXX:7001/PostTest:{"qqq":147}2020-01-13 09:16:58

http://XXXX:7002/PostTestNew:{"qqq":258}2020-01-13 09:16:58

7002/PostTestNew的結果中輸出的請求參數值發生了變化,但是從Session中讀取到的時間是7001/PostTest設置的Session值,而且訪問Redis數據庫,確實只保存了一個Session值,說明實現了Session共享。

 

 

 

最后尤其要注意,這里采用了cookie值作為id尋找Session值的方式,所以項目中需要保存第一次緩存Session產生的cookie值,在后面http請求的頭中帶上該cookie值;若是session值發生了變化,則將新的cookie值覆蓋到原來的cookie值。


免責聲明!

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



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