zoukankan      html  css  js  c++  java
  • .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值。

  • 相关阅读:
    RegularExpressions(正则表达式)
    IntelliJ Idea工具使用
    JavaScript总结
    Ajax技术基础
    配置Nginx作为反向代理服务器
    SpringBoot下的Dubbo和Zookeeper整合
    springSecurity初识练气初期
    Spring Security拦截器加载流程分析练气中期
    Spring Security认证流程分析练气后期
    springBoot整合spring security+JWT实现单点登录与权限管理前后端分离筑基中期
  • 原文地址:https://www.cnblogs.com/BradPitt/p/12188732.html
Copyright © 2011-2022 走看看