记录Dotnet6中使用Redis共享Session的实现

前言

因项目中实际需要,需要对dotnet服务做横向扩展,但是Session默认存储在MemoryCache中,无法实现多台服务器共享Session,导致通过nginx做负载均衡后出现跳登录的情况,因此需要使用Redis共享Session解决此问题。

实现

安装依赖

  • Microsoft.AspNetCore.DataProtection.StackExchangeRedis 6.0.35
  • Microsoft.Extensions.Caching.StackExchangeRedis 6.0.35

修改startup.cs/Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void ConfigureServices(IServiceCollection services)
{
// ... 其他中间件
services.AddHttpContextAccessor();
services.AddMvc().AddSessionStateTempDataProvider();

services.AddHttpClient();
// 注入Session
services.AddSessionService("CacheProvider");
// 使用Redis作为系统缓存
services.AddCacheService("CacheProvider");
services.AddControllersWithViews(ConfigureMvcOptions)
.AddNewtonsoftJson(options =>
{
options.UseMemberCasing();
// 格式化时间
options.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
});
// ... 其他中间件
}

services.AddSessionService(“CacheProvider”) 的实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public static void AddSessionService(this IServiceCollection services, string key)
{
using (ServiceProvider provider = services.BuildServiceProvider())
{
IConfiguration configuration = provider.GetRequiredService<IConfiguration>();
if (configuration == null)
{
throw new ArgumentNullException(nameof(IConfiguration));
}
IConfigurationSection section = configuration.GetSection(key);
if (!section.Exists())
{
Console.WriteLine($"appsetting.json 文件中不存在 '{key}' 配置项,如需使用redis缓存,请增加此配置。将使用系统缓存。");
}
CacheOptions options = section.Get<CacheOptions>();
if (options == null)
{
Console.WriteLine($"读取appsetting.json中'{key}'配置项失败,请正确配置。将使用系统缓存");
}

if (options?.CacheType == CacheTypeEnum.Redis.ToString())
{
Console.WriteLine($"将使用Redis缓存Session信息");

var connStr = options.RedisConnectionString;
var dic =
connStr.Contains(',') && !connStr.Contains(';') ?
connStr.SplitAsDictionary("=", ",", true) :
connStr.SplitAsDictionary("=", ";", true);

var Server = dic["Server"]?.Trim();
var UserName = dic["UserName"]?.Trim();
var Password = dic["Password"]?.Trim();

#region 配置说明

// var options = new ConfigurationOptions
// {
// AbortOnConnectFail = false, // 连接失败时是否中止
// AllowAdmin = false, // 是否允许使用管理命令
// ConnectTimeout = 5000, // 连接超时时间(毫秒)
// SyncTimeout = 5000, // 同步操作超时时间(毫秒)
// ResponseTimeout = 5000, // 响应超时时间(毫秒)
// ReconnectRetryPolicy = new LinearRetry(1000), // 重连策略
// DefaultDatabase = 0, // 默认数据库索引
// EndPoints = { "localhost:6379" }, // Redis节点的地址和端口
// Ssl = false, // 是否使用SSL加密连接
// SslHost = "localhost", // SSL连接时的主机名验证
// Password = "yourpassword", // Redis认证密码
// ClientName = "MyClient", // 客户端名称
// KeepAlive = 1800000 // 保持连接间隔时间(毫秒)
// };
#endregion

//获取Redis 连接字符串
ConfigurationOptions redisConfigOption = new ConfigurationOptions();
redisConfigOption.EndPoints.Add(Server);
redisConfigOption.AllowAdmin = false;
redisConfigOption.Password = Password;
redisConfigOption.ConnectTimeout = 60000;
redisConfigOption.ResponseTimeout = 60000;
redisConfigOption.SyncTimeout = 60000;
redisConfigOption.ReconnectRetryPolicy = new LinearRetry(1000);// 重连策略
var redis = ConnectionMultiplexer.Connect(redisConfigOption);//建立Redis 连接

//添加数据保护服务,设置统一应用程序名称,并指定使用Reids存储私钥
services.AddDataProtection()
.SetApplicationName("YZ")
.PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys");

//添加Redis缓存用于分布式Session
services.AddStackExchangeRedisCache(options =>
{
options.ConfigurationOptions = redisConfigOption;
options.InstanceName = "YZ";
});
}
//添加Session
services.AddSession(options =>
{
options.Cookie.Name = "YZ";
options.IdleTimeout = TimeSpan.FromMinutes(60 * 60); //设置session的过期时间
options.Cookie.HttpOnly = true; //设置在浏览器不能通过js获得该cookie的值
options.Cookie.IsEssential = true;
});

}
}

services.AddCacheService(“CacheProvider”); 实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

/// <summary>
/// 缓存注册(新生命Redis组件)
/// </summary>
/// <param name="services"></param>
public static void AddCacheService(this IServiceCollection services, string key)
{
using (ServiceProvider provider = services.BuildServiceProvider())
{
ICache cache = NewLife.Caching.Cache.Default;

IConfiguration configuration = provider.GetRequiredService<IConfiguration>();
if (configuration == null)
{
throw new ArgumentNullException(nameof(IConfiguration));
}
IConfigurationSection section = configuration.GetSection(key);
if (!section.Exists())
{
Console.WriteLine($"appsetting.json 文件中不存在 '{key}' 配置项,如需使用redis缓存,请增加此配置。将使用系统缓存。");
}
CacheOptions options = section.Get<CacheOptions>();
if (options == null)
{
Console.WriteLine($"读取appsetting.json中'{key}'配置项失败,请正确配置。将使用系统缓存");
}

if (options?.CacheType == CacheTypeEnum.Redis.ToString())
{
Console.WriteLine($"将使用Redis缓存");
var redis = new FullRedis();
redis.Init(options.RedisConnectionString);
cache = redis;

}

services.AddSingleton(cache);
}
}

按照以上的配置,就可以实现使用redis共享Session了。