## 场景划分


  • ResponseCaching:HTTP 层缓存(代理式),适合纯 GET、幂等且易于被代理缓存的端点。
  • IMemoryCache:单机内存缓存,延迟低但不跨实例;适合读多写少的热点数据。
  • IDistributedCache(Redis):跨实例共享缓存,适合多副本服务与会话/令牌等需要一致性的场景。

## 环境校验(可验证)


dotnet --info

Redis 可用性:`redis-cli PING` 返回 `PONG`;或 StackExchange.Redis 连接成功日志。


## ResponseCaching(可验证)


using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCaching();

var app = builder.Build();
app.UseResponseCaching();

app.MapGet("/products/{id}", (int id) =>
{
    return Results.Ok(new { id, ts = DateTimeOffset.UtcNow });
})
.RequireCors() // 如有跨域策略,可按需配置
.WithMetadata(new Microsoft.AspNetCore.Mvc.ResponseCacheAttribute
{
    Duration = 10, Location = Microsoft.AspNetCore.Mvc.ResponseCacheLocation.Any, NoStore = false
});

app.Run();

验证:对同一 `id` 在 10 秒内返回命中;注意 ResponseCaching 依赖合适的缓存相关响应头。


## IMemoryCache(可验证)


using Microsoft.Extensions.Caching.Memory;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMemoryCache();
var app = builder.Build();

app.MapGet("/profile/{id}", async (int id, IMemoryCache cache) =>
{
    if (!cache.TryGetValue($"profile:{id}", out object? value))
    {
        // 模拟数据源查询
        value = new { id, name = $"user-{id}", ts = DateTimeOffset.UtcNow };
        cache.Set($"profile:{id}", value, new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30),
            Size = 1 // 如启用 Size 限制需设置 Size
        });
    }
    return Results.Ok(value);
});

app.Run();

要点:


  • `AddMemoryCache` 可配合 `SizeLimit` 与逐出策略;适合单实例或状态粘滞的场景。
  • 写多或跨实例一致性要求高时不使用内存缓存作为真相源。

## IDistributedCache(Redis,可验证)


using Microsoft.Extensions.Caching.Distributed;
using StackExchange.Redis;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddStackExchangeRedisCache(o =>
{
    o.Configuration = builder.Configuration.GetConnectionString("Redis");
    o.InstanceName = "app:";
});

var app = builder.Build();

app.MapGet("/ticket/{id}", async (int id, IDistributedCache cache) =>
{
    var key = $"ticket:{id}";
    var bytes = await cache.GetAsync(key);
    if (bytes is null)
    {
        var payload = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(new { id, status = "ok", ts = DateTimeOffset.UtcNow });
        await cache.SetAsync(key, payload, new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60)
        });
        bytes = payload;
    }
    return Results.Bytes(bytes!, "application/json");
});

app.Run();

要点:


  • 使用 `AddStackExchangeRedisCache` 官方扩展,连接字符串通过配置注入。
  • 二进制存储更紧凑,减少序列化开销;跨实例共享命中。

## 热点失效与一致性(可验证)


  • 写操作后主动失效:对 `IMemoryCache` 使用 `Remove`,对 `IDistributedCache` 使用 `RemoveAsync`;在复杂聚合场景使用 Key 前缀或 Tag 方案进行批量失效。
  • 避免“缓存击穿”:对热点 Key 使用互斥/单航(single-flight)逻辑;或短期降级(返回旧值)。

## 参数选择建议


  • 单实例:优先 `IMemoryCache`,配合过期与大小限制。
  • 多实例:优先 `IDistributedCache`(Redis),与连接池/超时、重试策略匹配;读多写少端点可叠加 ResponseCaching。

## 压测建议(可验证)


  • 对 `/profile/{id}`、`/ticket/{id}` 分别进行 60–120 秒压测,记录命中率、`RPS` 与 `p95/p99`,对比冷/热数据阶段表现。
  • 在 Redis 场景下观察连接数、慢查询与网络抖动;评估序列化格式与帧大小对时延的影响。

## 注意事项


  • 缓存不是持久层:不要将缓存作为权威数据源;写路径需保证一致性。
  • 不要缓存高度个性化且强时效的数据(例如一次性令牌),以免泄漏与过期风险。
  • 在生产前于预演环境验证参数与失效策略,确保观测数据与报警阈值到位。

## 结语


合理组合 ResponseCaching、IMemoryCache 与 IDistributedCache(Redis),可在不同层面消除重复计算与 IO,显著改善吞吐与时延。坚持“可验证”的方法与端到端监控,才能稳定地将收益带入生产。



点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部