Redis 缓存(Aegis.Caching.Redis)
Aegis.Caching.Redis 是 Aegis 中与 Redis 交互的基础能力包。它负责提供 RedisSourceBase、脚本管理和多级缓存所依赖的底座能力。它不通过 Component.deps.json 自动装配,而是由项目在代码里显式注册。
组件概览
| 字段 | 说明 |
|---|---|
| 组件名称 | Redis 缓存 |
| 真实类库 | Aegis.Caching.Redis |
| 组件定位 | Redis 数据源、脚本管理与缓存扩展底座 |
| 引入方式 | 安装 NuGet,并在代码中调用 AddRedisSource<T>() |
是否需要 Component.deps.json | 否 |
| 配置节点 | Redis |
| 核心能力 | Redis 数据源、脚本统一加载、故障降级参数、本地 L1 缓存配置 |
| 注册入口 | AddRedisSource<T>() |
什么时候要用它
适合场景:
- 业务服务需要直接访问 Redis
- 认证会话要切到 Redis
- ID 生成器要接 Redis 容器
- 事件总线要改成 Redis 容器
- 需要统一管理 Lua 脚本
- 需要给缓存拦截器或多级缓存提供 Redis 底座
先记住它和其他 Redis 相关组件的关系
| 组件 | 负责什么 |
|---|---|
Aegis.Caching.Redis | Redis 基础能力 |
Aegis.Authorization.RedisUserManager | Redis 会话 |
Aegis.IdGenerator.Provider.Redis | ID 生成器 Redis 容器 |
Aegis.EventBus.Container.Redis | 事件总线 Redis 容器 |
也就是说,很多 Redis 能力最终都依赖当前组件先把数据源接起来。
当前这层还直接提供两类常见上层能力:
- 基于脚本的
BiLocker - 基于拦截器的多级缓存
最小可运行路径
第一步:准备 Redis 配置
{
"Redis": {
"RedisMode": "Standalone",
"ConnectionString": "127.0.0.1:6379,defaultDatabase=2",
"SentinelString": "",
"IsRWSplitting": true,
"ScriptPath": "{CurrentDirectory}/Scripts/",
"Resilience": {
"Score": 5,
"RecoverScore": 3,
"HeartBeatInterval": 5,
"FallbackPolicy": "Fallback"
},
"L1Cache": {
"Enabled": true
}
}
}
第二步:定义自己的 RedisSource
public class AegisRedisSource : RedisSourceBase
{
public AegisRedisSource(RedisOptions redisOptions) : base(redisOptions)
{
}
}
第三步:在服务注册阶段显式注入
services.AddRedisSource<AegisRedisSource>(
ConfigManager.Get<RedisOptions>("Redis"));
第四步:在业务类中注入 AegisRedisSource
public class UserService : IUserContract
{
private readonly AegisRedisSource _redis;
public UserService(AegisRedisSource redis)
{
_redis = redis;
}
}
这组配置分别控制什么
| 配置项 | 作用 |
|---|---|
RedisMode | Redis 模式,支持 Standalone、MasterSlave、Sentinel、Cluster |
ConnectionString | 主连接串,单实例或多节点地址都写在这里 |
SentinelString | 哨兵模式下的补充参数 |
IsRWSplitting | 是否开启读写分离 |
ScriptPath | Lua 脚本目录 |
Resilience.Score | Redis 进入降级前的分值上限 |
Resilience.RecoverScore | 从降级恢复时需要达到的分值 |
Resilience.HeartBeatInterval | 心跳检测间隔,单位秒 |
Resilience.FallbackPolicy | 故障策略,通常是 Fallback 或 Throw |
L1Cache.Enabled | 是否开启本地一级缓存 |
最常见的三种配置场景
单实例
{
"Redis": {
"RedisMode": "Standalone",
"ConnectionString": "127.0.0.1:6379,defaultDatabase=2",
"SentinelString": "",
"IsRWSplitting": false,
"ScriptPath": ""
}
}
主从
{
"Redis": {
"RedisMode": "MasterSlave",
"ConnectionString": "10.0.0.11:6379;10.0.0.12:6379,defaultDatabase=2",
"SentinelString": "",
"IsRWSplitting": true,
"ScriptPath": ""
}
}
哨兵
{
"Redis": {
"RedisMode": "Sentinel",
"ConnectionString": "10.0.0.11:26379;10.0.0.12:26379;10.0.0.13:26379",
"SentinelString": "mymaster,defaultDatabase=2",
"IsRWSplitting": true,
"ScriptPath": "{CurrentDirectory}/Scripts/"
}
}
最常见的使用方式
直接使用 RedisSource 的字符串操作
public class UserCacheService
{
private readonly AegisRedisSource _redis;
public UserCacheService(AegisRedisSource redis)
{
_redis = redis;
}
public async Task SetUserAsync(string key, UserDto user)
{
await _redis.SetAsync(key, user, 300);
}
public async Task<UserDto?> GetUserAsync(string key)
{
return await _redis.GetAsync<UserDto>(key);
}
public async Task RemoveUserAsync(string key)
{
await _redis.DelAsync(key);
}
}
在集合里增删成员
await _redis.SAddAsync("role:admin", userId);
await _redis.SRemAsync("role:admin", userId);
var diff = await _redis.SDiffAsync("role:admin", "role:guest");
操作哈希字段
await _redis.HSetAsync("doctor:1001", "name", "张三");
var doctorName = await _redis.HGetAsync<string>("doctor:1001", "name");
var hasName = await _redis.HExistsAsync("doctor:1001", "name");
脚本管理怎么用
RedisSourceBase 会暴露 Scripts 管理器。
如果 ScriptPath 有值,启动时会把目录下的 *.lua 文件统一加载。
通过目录自动加载
{
"Redis": {
"ScriptPath": "{CurrentDirectory}/Scripts/"
}
}
手动追加脚本文件
_redis.Scripts.AddScriptByPath("C:\\Scripts\\GetBiLocker.lua");
手动追加脚本文本
_redis.Scripts.AddScript("GetBiLocker", luaContent);
执行脚本
var result = _redis.Scripts.ExecuteScript(
"GetBiLocker",
"lock:key",
new object[] { "inputKey", 30 });
BiLocker 在哪一层用
BiLocker 是建立在 Scripts 管理器上的一组扩展方法,适合需要双边互斥或成对绑定关系的场景。
var isLocked = await _redis.Scripts.SetBiLock(
"BedLock",
key1,
key2,
timeout: 30,
isForce: false,
side: BiLockSide.Left);
var removed = await _redis.Scripts.RemoveBiLock(
"BedLock",
key1,
BiLockSide.Left);
如果你准备使用它,先确保 ScriptPath 对应目录已经存在,且脚本能在启动时被正常加载。
多个 Redis 怎么接
如果一个项目里要连多个 Redis,不要共用一个 RedisSource,而是分别定义自己的实现类。
public class BizRedisSource : RedisSourceBase
{
public BizRedisSource(RedisOptions redisOptions) : base(redisOptions)
{
}
}
public class SessionRedisSource : RedisSourceBase
{
public SessionRedisSource(RedisOptions redisOptions) : base(redisOptions)
{
}
}
然后分别绑定不同配置节点。
services.AddRedisSource<BizRedisSource>(
ConfigManager.Get<RedisOptions>("BizRedis"));
services.AddRedisSource<SessionRedisSource>(
ConfigManager.Get<RedisOptions>("SessionRedis"));
如果要开启多级缓存,还要补什么
当前 Redis 组件本身只是底座。
如果你们要继续往“L1 本地缓存 + L2 Redis”推进,通常还要再补:
services.AddMemoryCache()- 缓存拦截器
- 对应的缓存注解或策略配置
这条路线更适合缓存方案已经稳定的场景,不建议在首轮接 Redis 时一起把所有缓存拦截都堆上去。
更完整的接入方式见 多级缓存。
接入完成后怎么确认成功
你至少应该确认这些点:
ConfigManager.Get<RedisOptions>("Redis")能正常读取到配置AddRedisSource<T>()已执行- 容器内可以正常注入你的
RedisSource RedisSource暴露的方法能完成一次最小读写- 如果配置了
ScriptPath,启动时不会因为目录错误直接报错
常见问题
为什么注入不到 AegisRedisSource
最常见原因是服务注册阶段没有执行 AddRedisSource<AegisRedisSource>(...)。
为什么一启动就报脚本路径错误
如果 ScriptPath 非空,运行目录里必须真的存在该目录。
如果当前项目没有 Lua 脚本,把它设为空字符串最稳。
为什么 Redis 相关组件接了,但会话或事件总线还是不工作
因为很多扩展组件只是依赖 Redis 底座,并不会自动完成自己的上层注册。
例如 Redis 会话、Redis 事件总线、Redis ID 容器都还需要各自继续接。