.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<string>("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<CookiePolicyOptions>(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";
}
<h2>Index</h2>
@ViewData["obj"]
<hr/>
@ViewData["RedisSession"]
Page2.cshtml
@{
ViewData["Title"] = "Page2";
}
<h2>Page2</h2>
@ViewData["obj"]
<hr />
@ViewData["RedisSession"]
_Layout.cshtml 下面加 ServerIP,刷新页面的时候就能看到负载到哪台服务器上
@ViewData["ServerIP"]
3.nginx负载均衡
http里加upstream
upstream lb{
server 192.168.2.125:5000;
server 192.168.2.115: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.125:5000;
server 192.168.2.115: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的形式存储。