缓存后端切换说明(本地内存 ⇄ Redis,可插拔抽象)
2026/8/12大约 3 分钟
缓存后端切换说明(本地内存 ⇄ Redis,可插拔抽象)
项目新增了一套可插拔缓存抽象 CacheStore(泛型 ICache),供新功能统一使用。 缓存后端支持两种实现,且通过配置即可切换,无需改动业务代码。
老的
CacheHelper(及所有旧调用点:权限、扫码登录、验证码、短信码、数据权限等)保持不变, 仍走原有本地内存。本抽象仅服务于新功能(如单设备登录会话缓存)。
一、两类 Redis 连接 + 两种后端
项目 RedisServer 已分出两个连接(见 appsettings.RedisServer):
| 连接串 | 语义 | 命名空间枚举 |
|---|---|---|
RedisServer:Cache | 业务缓存库(默认) | CacheBackend.Cache |
RedisServer:Session | 会话库(会话态数据) | CacheBackend.Session |
每个连接又可在「本地内存 / Redis」间自动切换:
| 后端 | 实现类 | 适用场景 | 跨实例共享 |
|---|---|---|---|
| 本地内存(默认) | MemoryCacheStore | 单实例 / 测试 / 内网 | 否 |
| Redis | RedisCacheStore | 多实例负载均衡、公网部署 | 是 |
二、如何切换(直接改配置)
后端由 CacheStore 门面在运行时自动选择:
- 对应 Redis 连接已初始化(
RedisServer:open = 1)→ 使用 Redis(按CacheBackend选Cache/Session库); - 否则 → 使用 本地内存。
只需修改 ZR.Admin.WebApi/appsettings.json 中的 RedisServer:open:
"RedisServer": {
"open": 1, // 改为 1 即启用 Redis 后端(新缓存自动切换到 Redis)
"dbCache": false,
"Cache": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
"Session": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
}
启用后
Program.cs会调用RedisServer.Initalize()初始化RedisServer.Cache与RedisServer.Session, 新功能缓存随即走对应 Redis 库,单设备登录即可在多个部署节点间生效。
三、接口设计(可扩展、泛型)
抽象定义在 Infrastructure/Cache/CacheStore.cs(命名空间 Infrastructure.Cache,符合 ZR.Infrastructure 类库约定;其引用的 RedisServer 也已统一到 Infrastructure.Cache 命名空间):
public enum CacheBackend { Cache, Session }
public interface ICache
{
T Get<T>(string key);
void Set<T>(string key, T value, int expireMinutes);
bool Exists(string key);
void Remove(string key);
}
public class MemoryCacheStore : ICache { /* 复用旧的 CacheHelper,本地内存 */ }
public class RedisCacheStore : ICache { /* 基于 CSRedisClient,默认 JSON 序列化 */ }
public static class CacheStore
{
// 按 CacheBackend 选择 Redis 连接;对应连接为 null 时回退本地内存
public static ICache For(CacheBackend backend) { ... }
public static ICache Default => For(CacheBackend.Cache); // 业务缓存默认走 Cache 库
// 便捷泛型方法(默认走 Cache 库)
public static void Set<T>(string key, T value, int expireMinutes) => Default.Set(...);
public static T Get<T>(string key) => Default.Get<T>(key);
}
MemoryCacheStore直接复用旧CacheHelper,不改动CacheHelper源码,Get 走非泛型重载再强转, 从而绕开CacheHelper.GetCache<T>的class约束,值类型也能存取。RedisCacheStore基于CSRedisClient,存对象走默认 JSON 序列化,跨节点共享。- 业务侧(
CacheService.SetUserSession / GetUserSession / RemoveUserSession)只依赖CacheStore门面, 新增其他后端(如 Memcached)只需实现ICache并在CacheStore中增加分支即可。
四、新功能如何使用
// 业务缓存:默认走 Cache 库
CacheStore.Set("mykey", myObject, 30);
var obj = CacheStore.Get<MyType>("mykey");
CacheStore.Remove("mykey");
// 会话态数据:显式走 Session 库(如单设备登录)
CacheStore.For(CacheBackend.Session).Set("session_key", sessionId, 30);
var sid = CacheStore.For(CacheBackend.Session).Get<string>("session_key");
单设备登录会话缓存即使用 CacheBackend.Session,与项目“会话”语义一致,且不和业务缓存混库。
五、多实例部署要点
- 单设备登录要“跨节点”生效,必须将缓存后端切到 Redis(
RedisServer:open = 1),会话数据落Session库。 - 缓存 key 建议沿用
CacheService.BuildTenantKey(...)做「租户」隔离,多租户环境安全。 - 过期时间单位为分钟,Redis 后端内部换算为秒(
TimeSpan.FromMinutes)。 - 若 Redis 不可用(初始化失败或
open=0),RedisServer.Session为 null,自动回退本地内存, 此时单设备登录退化为单节点生效,不影响系统其余功能。
六、验证建议
- 配置
SingleLogin: true、RedisServer:open: 1并启动(多实例或多标签页模拟)。 - 设备 A 登录 → 设备 B 登录同一账号。
- 设备 A 发起任意请求 → 应收到
code = 480并被前端踢回登录页。 - 关闭 Redis(
open: 0)重启 → 单设备登录仅在各节点内部生效,用于对比验证。
