Skip to the content.

Redis 共享缓存

MS.Microservice.Caching.Redis 为缓存治理接入 Redis,使不同应用实例能够读取同一份缓存数据。业务继续使用 IGovernedCache,无需改成 Redis 专用读写 API。

层级 存储 用途
L1 每个应用实例中的 MemoryCache 减少网络访问,进程重启后丢失
L2 通过 IDistributedCache 接入的 Redis 在实例间共享序列化结果
数据源 调用方提供的加载器 两级未命中时读取权威数据

只有 L1 时也是可用的缓存;注册本包可为治理层补上 Redis L2。AddDistributedMemoryCache 的数据仍留在当前进程,不能代替 Redis 的跨进程共享能力。

注册

宿主引用本包,在 DI 中注册一次。Redis 连接从环境变量或宿主的秘密配置中提供,不写入源代码。

$env:ConnectionStrings__CacheRedis = '<Redis 连接配置>'
using MS.Microservice.Caching;
using MS.Microservice.Caching.Redis;

builder.Services.AddGovernedRedisCaching(
    configureRedis: redis =>
    {
        redis.Configuration = builder.Configuration.GetConnectionString("CacheRedis");
        redis.InstanceName = "cache:";
    },
    configureCaching: cache =>
    {
        cache.ApplicationName = "Orders";
        cache.ExpirationSeconds = 300;
        cache.LocalExpirationSeconds = 30;
        cache.MaximumConcurrentLoads = 32;
    });

此入口同时注册 Redis provider、HybridCache 和治理层,不要再调用 AddStackExchangeRedisCache 或 AddGovernedCaching。启用时若已经注册 IDistributedCache 或 IGovernedCache,会拒绝重复注册。

配置 默认/要求
RedisCacheOptions.Configuration Redis 连接配置;也可使用带端点的 ConfigurationOptions,必须有有效端点
RedisCacheOptions.InstanceName 默认 cache:;非空且最多 128 个字符,作为 Redis 键前缀
RedisCacheOptions.ConnectionMultiplexerFactory 本适配不接受外部连接工厂,连接由官方 provider 独占管理
CachingOptions.Enabled 默认 true;false 时不执行 Redis 配置回调,不注册 Redis/HybridCache,读取直接调用源
两级 TTL、负缓存、回源预算、大小限制 沿用 Caching 的配置与约束

连接字符串采用 StackExchange.Redis 语法,例如 localhost:6379,ssl=false。它不接受部分其他客户端的 poolsize、prefix 参数:键前缀放到 InstanceName,并发命令通过长连接复用处理,无需设置连接池大小。连接参数说明

Redis Cluster 可以从主节点或副本 seed 开始发现拓扑;客户端还需访问集群公布的其他节点地址。仅 seed 可达不代表能够路由所有键,应确保宿主能连接到各分片公布的端点。Cluster 使用数据库 0,不能按逻辑数据库编号隔离业务。

注册只检查配置,不证明 Redis 已连接。官方 provider 在实际缓存操作时建立并复用长连接;宿主释放容器时释放它。本适配不会注册可供其他组件借用的 IConnectionMultiplexer。连接所有权及健康检查的组合见设计文档。

读写与失效

注入 IGovernedCache,与本地模式使用同一套键和 API:

var key = cache.CreateKey<ProductView>("products.v1", trustedTenant, "p-1");
var product = await cache.GetOrCreateAsync(key,
    token => ValueTask.FromResult(new ProductView("p-1", "示例商品")),
    cancellationToken);

// 业务事务提交成功之后,使用相同的键失效。
await cache.RemoveAsync(key, cancellationToken);

// 已有完整 DTO,明确需要主动填充时:
await cache.SetAsync(key, product, cancellationToken);

public sealed record ProductView(string Id, string Name);

加载器使用传入的取消令牌,并自行管理数据库等依赖的生命周期。同实例同键可能合并请求,不应捕获随首个请求释放的 DbContext。缓存独立 DTO,避免共享跟踪实体;完整使用案例见 Caching README。

多个实例要共享值,应使用相同的 Redis 部署/数据库、InstanceName、应用名、region、类型和可信 Scope,且能兼容同一种序列化格式。region 中保留数据格式版本;修改 DTO 契约后更换版本,避免新旧服务误读。

源生成 JSON

需要显式 JSON 元数据时,直接使用第三个参数:

using System.Text.Json.Serialization;

builder.Services.AddGovernedRedisCaching(
    redis => redis.Configuration = builder.Configuration.GetConnectionString("CacheRedis"),
    cache => cache.ApplicationName = "Orders",
    serialization: serializers =>
        serializers.AddGovernedJson(ProductJsonContext.Default.ProductView));

[JsonSerializable(typeof(ProductView))]
internal partial class ProductJsonContext : JsonSerializerContext;

为需要显式序列化的类型分别注册元数据。这提供确定的序列化入口,不代表整个宿主已完成 Native AOT/裁剪发布验证。Redis 中的 payload 由 HybridCache 和 provider 管理,不应从业务代码直接用 Redis 字符串命令读写这些键。

TTL、一致性与故障

默认本地 TTL 为 30 秒,共享 TTL 为 300 秒,要求 0 < 本地 TTL <= 共享 TTL。本地过期后先查 Redis;Redis 也未命中时才执行加载器。读取共享值不会把其整体有效期无限延长。

RemoveAsync 清除当前实例 L1 和 Redis L2,不清除其他实例已经持有的 L1。其他实例可能持续返回旧副本直至本地过期,已经开始的旧加载也可能回填。本包没有失效广播、跨实例加载锁或数据库与缓存的共同事务;实时授权等严格新鲜的查询应绕过缓存。

Redis 不可用时,已有 L1 命中仍可使用。当前 HybridCache 对普通 L2 读错误记录日志后按未命中回源,对回填 L2 的错误记录日志并保留可用的本地结果。取消和业务加载异常仍需由调用方处理;读取成功不等于 Redis 回填成功。

MaximumConcurrentLoads 仅限制单实例正在执行的源工厂,耗尽抛出 CacheLoadLimitException。它不限制等待 Redis 的请求数,不缩短连接/命令超时,也不提供跨实例限流或熔断。宿主应按自身服务目标配置 Redis 超时、过载响应和故障恢复,失效失败也需要可靠通知或后续核对。

监测

继续使用治理层的 MS.Microservice.Caching Meter;本包不再创建一套逐键状态或重复指标。cache.requests、cache.source.loads、cache.source.rejected 和 cache.source.duration 的口径及 dotnet-counters/OpenTelemetry 接入见 Caching 监测说明。

已有 Observability 注册会订阅此 Meter。它反映治理请求与回源行为,不能提供精确的 L1/L2 命中率、Redis 命令耗时、连接状态或服务端容量;这些信息应结合官方缓存日志和 Redis 侧监测。

在 Lab 体验

Lab 默认 Caching:Backend=Memory。改用 Redis 时,在环境变量提供 ConnectionStrings__CacheRedis,并设置:

{
  "Caching": {
    "Enabled": true,
    "Backend": "Redis",
    "ApplicationName": "MS.Microservice.Lab",
    "ExpirationSeconds": 300,
    "LocalExpirationSeconds": 30,
    "Redis": { "InstanceName": "lab:cache:" }
  }
}

Backend 和 Redis:InstanceName 是 Lab 的组合配置。独立宿主使用前面的注册入口;配置更改后重启应用。

登录后连续调用 GET /api/lab/tools/cache,再通过 PUT 更新和 DELETE 失效,观察回源次数与指标。Lab 示例源保存在进程内,重启会重置;它用于体验 API,不作为多实例共享业务源或 Redis 容量验收。更多步骤见 Lab 缓存治理示例。

已有缓存微基准衡量治理层的本地开销,不能用于推断 Redis 吞吐、网络延迟或故障恢复能力。真实部署需分别测量 L1 命中、L2 读取和回源;实现责任与性能边界见设计文档。

当前测试环境的实库验证、L1 增量与 L2 读取结果见 Redis 性能记录。