.NET Core中使用Redis做分布式缓存及Session共享

场景:
众所周知,一般session都存在服务器内存中,一般情况下两台服务器的内存不能共享。
比如网站做集群的时,用户访问服务器1上的网站1生成一个Session;当负载均衡到服务器上的网站2时,Session无法跟过去。
缓存也是一样的,.NET的自带的缓存机制将缓存存储在服务器内存中,在iis中应用程序池一回收,缓存就没了。

 

方案:
这时可以借助第三方的组件来完成跨服务器的访问,比如Redis或其它数据库等,将这些状态存在第三方,要用时都去这个第三方取,自然就能保持状态一致了。

 

例:
这里以.NET Core2.2为例。2.1我折腾了下始终有点问题,用2.2就正常了。

 

集群:
网站部在两台CentOS 7 64bit上
192.168.2.112:5000 网站
192.168.2.113:5000 网站

用nginx做负载均衡,nginx也在112上原来有,没有单独部一台,把网站负载到8888端口
192.168.2.112:8888 网站

Redis装在windows上(因为win上之前装过,就直接用了):
192.168.2.223:6379
 

服务器 部署 系统
192.168.2.112:5000 网站 CentOS 7 64bit
192.168.2.113:5000 网站 CentOS 7 64bit
192.168.2.112:8888 nginx负载均衡 CentOS 7 64bit
192.168.2.223:6379 Redis Windows

 

 


0.Redis安装

 

1.SDK
Visual Studio没有.NET Core 2.2的话,先下载sdk装下,sdk不大,下载地址 https://dotnet.microsoft.com/download
我用的是Visual Studio 2017所以下的是左边第三个。安装完在Visual Studio相关项目中点属性把 Core改成2.2的。

 

2.代码
2.1 appsettings.json中添加redis节点,形如

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "redis": {
    "host": "192.168.2.223:6379",
    "name": "redisInstance1_"
  }
}

 

2.2 用Nuget包管理搜索安装
StackExchange.Redis
Microsoft.Extensions.Caching.StackExchangeRedis

 

2.3 StartUp.cs

添加AddStackExchangeRedisCache、AddSession

using Microsoft.AspNetCore.DataProtection;
using StackExchange.Redis;


        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //-----------redis分布式缓存相关------------------
            //services.AddMvc();

            var redisConnection = Configuration.GetValue("redis:host");// Configuration.GetConnectionString("RedisConnection");
            services.AddDataProtection()
                .SetApplicationName("WebAppDotNetCore22")
                .PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(redisConnection), "DataProtection-Keys");


            services.AddStackExchangeRedisCache(o =>
            {
                o.Configuration = redisConnection;
            });

            services.AddSession(o =>
            {
                o.Cookie.Name = "WebAppDotNetCore22.Session";//设置一个cookie名,session要使用cookie
                o.Cookie.SameSite = SameSiteMode.None;
                o.Cookie.HttpOnly = true;//只能从http端获取,增加安全性
                o.IdleTimeout = TimeSpan.FromMinutes(10);
            });


            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            //-----------redis分布式缓存相关------------------

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


  Configure方法里添加 app.UseSession();

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            //-----------------------------
            app.UseSession();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }


 

2.4 新建一个TestCacheController的控制器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;

namespace WebApplicationNetCore.Controllers
{
    public class TestCacheController : Controller
    {
        private readonly IDistributedCache _cache;
        public TestCacheController(IDistributedCache cache)
        {
            _cache = cache;           
        }

        string cacheKey = "cache_";
        string redisSessionKey = "session_";

        public IActionResult Index()
        {
            var exists = true;
            var tmpObj = _cache.Get(cacheKey);//获取缓存
            if (tmpObj == null)
            {
                exists = false;
                _cache.Set(cacheKey, Encoding.Default.GetBytes("Redis Cache"));//把数据添加到缓存
                tmpObj = _cache.Get(cacheKey);
               
            }
            var val = Encoding.Default.GetString(tmpObj);
            var obj = new {
                exists,
                val,
            };

            ViewData["obj"] = JsonConvert.SerializeObject(obj);


            //------------------------------------
            var RedisSession = Encoding.Default.GetBytes("Redis Session");//转成byte[]格式,redis中数据都是以byte[]格式存储
            HttpContext.Session.Set(redisSessionKey, RedisSession);//设置session

            ViewData["RedisSession"] = HttpContext.Session.TryGetValue(redisSessionKey, out byte[] getRedisSession) ? Encoding.Default.GetString(getRedisSession) : "";//获取session,并转为字符串

            ViewData["ServerIP"] = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString();
            return View();
        }

        public IActionResult Page2()
        {   
            ViewData["obj"] = "";
            var tmpObj = _cache.Get(cacheKey);
            if (tmpObj != null)
                ViewData["obj"] = Encoding.Default.GetString(tmpObj);
            

            ViewData["RedisSession"] = HttpContext.Session.TryGetValue(redisSessionKey, out byte[] getRedisSession) ? Encoding.Default.GetString(getRedisSession) : "";

            ViewData["ServerIP"] = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString();
            return View();
        }
    }
}


 

2.5 View

Index.cshtml

@{
    ViewData["Title"] = "Index";
}

Index

@ViewData["obj"]


@ViewData["RedisSession"]

 

Page2.cshtml

@{
    ViewData["Title"] = "Page2";
}

Page2

@ViewData["obj"]


@ViewData["RedisSession"]

 

_Layout.cshtml 下面加 ServerIP,刷新页面的时候就能看到负载到哪台服务器上

@ViewData["ServerIP"]


 

3.nginx负载均衡
http里加upstream

    upstream lb{
        server 192.168.2.112:5000;
        server 192.168.2.113:5000;
    }
    
 形如:

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    upstream lb{
        server 192.168.2.112:5000;
        server 192.168.2.113:5000;
    }

    include /etc/nginx/conf.d/*.conf;
}

server里加
 listen       8888;
 proxy_pass    http://lb;
 形如:

server {
    listen       8888;
    server_name  localhost;

    location / {
        proxy_pass    http://lb;
        proxy_http_version 1.1;    
        proxy_set_header Upgrade $http_upgrade;    
        proxy_set_header Connection "Upgrade";
    }


    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

} 

重启nginx

 

4.效果


a.访问
http://192.168.2.113:8888/TestCache

看尾部的服务器是113

 

b.再访问
http://192.168.2.113:8888/TestCache/Page2


看尾部的服务器是112
cache和session都取出来了

 

c.查看Redis

redis中能看到两个key一个存sesssion,一个是缓存。都是以hash的形式存储。

类别:DotNET   阅读(0)   评论(0)    发表时间:2019-07-17 22:45  星期三

评论区

发表评论

        姓名:
邮箱|网站:
        内容:

  (可按Ctrl+Enter提交)