跳到主要内容

IGeekFan.AspNetCore.OutputCaching.FreeRedis

ASP.NET Core 输出缓存(Output Caching)的 FreeRedis 分布式存储实现,将缓存内容写入 Redis,支持多实例共享与按标签(Tag)失效。

对应 ASP.NET Core 内置的 Microsoft.AspNetCore.OutputCaching 中间件(.NET 7+),并非旧的 Response Caching。

功能概览

功能说明
分布式存储输出缓存内容存入 Redis,多实例共享
标签失效按 Tag 批量过期缓存(EvictByTagAsync
连接复用可复用已有的 FreeRedis IRedisClient
键前缀通过 InstanceName 隔离不同应用

支持的框架与依赖

目标框架net8.0net10.0
主要依赖FreeRedis 1.5.5、Microsoft.Extensions.Options
集成Microsoft.AspNetCore.OutputCaching(框架自带)

安装

dotnet add package IGeekFan.AspNetCore.OutputCaching.FreeRedis

快速开始

场景:多实例 Web 部署

1. 配置 Redis

{
"ConnectionStrings": {
"Redis": "127.0.0.1:6379,password=***,defaultDatabase=2"
}
}

2. 注册服务

需要先调用框架内置的 AddOutputCache(),再用本包把存储替换为 FreeRedis:

using FreeRedis;
using IGeekFan.AspNetCore.OutputCaching.FreeRedis;

var builder = WebApplication.CreateBuilder(args);

// 1. 启用输出缓存(框架内置)
builder.Services.AddOutputCache(options =>
{
// 默认策略:缓存 60 秒
options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromSeconds(60)));
});

// 2. 用 FreeRedis 作为分布式存储
builder.Services.AddFreeRedisOutputCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "MyApp:"; // 可选:键前缀,隔离不同应用
});

var app = builder.Build();

app.UseOutputCache();

app.MapGet("/weather", () => Results.Ok(new { Temp = 26 }))
.CacheOutput(); // 该路由走输出缓存

app.Run();

3. 在 Controller 上启用

[ApiController]
[Route("api/[controller]")]
public class ArticleController : ControllerBase
{
// 缓存 30 秒,并打上 "articles" 标签
[HttpGet("{id}")]
[OutputCache(Duration = 30, Tags = new[] { "articles" })]
public async Task<IActionResult> Get(Guid id) => Ok(/* ... */);
}

场景:复用已有 FreeRedis 客户端

如果项目中已经有一个 IRedisClient(例如 SignalR、DataProtection 共用),可直接传入,避免重复建连:

using FreeRedis;
using IGeekFan.AspNetCore.OutputCaching.FreeRedis;

var builder = WebApplication.CreateBuilder(args);

var redis = new RedisClient(builder.Configuration.GetConnectionString("Redis"));
builder.Services.AddSingleton<IRedisClient>(redis);

builder.Services.AddOutputCache();

// 复用现有客户端(无需 Configuration,可选 InstanceName)
builder.Services.AddFreeRedisOutputCache(redis, options =>
{
options.InstanceName = "MyApp:";
});

按标签失效

缓存写入时会把 Tags 维护到 Redis 的 tag: 集合中;调用 EvictByTagAsync 即可批量清除:

// 手动按标签失效(例如在内容更新后)
public class ArticleManager
{
private readonly IOutputCacheStore _store;

public ArticleManager(IOutputCacheStore store) => _store = store;

public async Task InvalidateAsync(CancellationToken ct)
{
// 清除所有带 "articles" 标签的缓存条目
await _store.EvictByTagAsync("articles", ct);
}
}

也可通过框架内置的 IOutputCacheStore 在中间件/过滤器中统一触发,无需直接引用本包类型。

Redis 键结构

前缀说明
缓存值{InstanceName}oc:{key}序列化后的响应内容
标签集合{InstanceName}tag:{tag}该标签关联的缓存值键集合(Redis Set)

InstanceName 为空时前缀省略,默认即 oc: / tag:

配置选项

选项类型说明必填
Configurationstring?FreeRedis 连接字符串;使用 IRedisClient 重载时可不填否*
InstanceNamestring?所有键的前缀,用于多应用隔离

* 仅在使用 Action<FreeRedisOutputCacheOptions> 重载且未传入 IRedisClient 时,Configuration 为必填;缺失会抛出 InvalidOperationException

运维建议

建议说明
独立 Redis DB缓存分配独立 Redis DB,避免与业务数据相互干扰
合理设置过期通过 Duration/Expire 控制 TTL,避免脏数据长期驻留
多实例一致所有实例使用同一 Redis,保证缓存视图一致
高可用生产环境启用 Redis 持久化与哨兵/集群
监控内存关注 Redis 内存与键数量,必要时用 Tag 精准失效

故障排查

问题解决方案
Configuration must be provided使用 Action<> 重载时必须设置 FreeRedisOutputCacheOptions.Configuration
缓存未生效确认已调用框架的 AddOutputCache() 且管道中有 UseOutputCache()
多实例不一致检查各实例是否指向同一 Redis 实例与相同 InstanceName
内存增长检查 TTL 设置,或使用 Tag 主动失效

相关文档