XiHan Framework 处于预览阶段

查看获取方式
跳转到内容

XiHan.Framework.Caching.Hybrid ​

两级缓存 Provider:提供 caching.local@1,把 ICache 换成「L1 进程内对象缓存 + L2 被选中的共享缓存」,读路径少一次网络往返,适用于多实例部署下读多写少的数据。

项内容
包 IDXiHan.Framework.Caching.Hybrid
环层Ring 3(实现)
能力提供 caching.local@1(role: provider);依赖 kernel.hosting@1、caching.distributed@1
特性入口HybridCachingFeature(清单 ID xihan.caching.hybrid)
配置节XiHan:Caching:Hybrid
依赖XiHan.Framework.Caching.Abstractions;无第三方依赖
AOT兼容
稳定性Preview

概述 ​

HybridCache 实现 ICache:L1 是有界 LRU 的对象引用表,L2 是容器里的 ISharedCache(Redis、Remote,或单实例下内存实现提供的共享缓存)。ISharedCache 本身不变,仍直接指向 L2。

操作行为
GetAsync<T>L1 命中直接返回;未命中读 L2,命中后回填 L1
GetManyAsync<T>先在 L1 取全部,只把未命中的键一次交给 L2
SetAsync<T> / SetManyAsync<T>先写 L2 再写 L1;L2 失败时 L1 不留下别的实例看不到的值
RemoveAsync / RemoveManyAsync先删 L2 再删 L1;两级任一存在过即报告删到
GetOrCreateAsync<T>L1 未命中时交给 L2 的 GetOrCreateAsync(两级都未命中才执行工厂),结果回填 L1

L1 里每个条目的存活时间是它自己的过期时长与 LocalTimeToLive 的较小者,滑动过期在 L1 里按绝对时间处理;从 L2 回填的条目存活 LocalTimeToLive。L1 存对象引用,调用方不得修改取回的对象;ICache.Store 是 L2 的字节存储。

跨实例失效(ADR-0055):L2 的存储实现了 ICacheChangeFeed(Redis)时 HybridCache 订阅它,本实例与其他实例的写入、删除、按前缀删除都让 L1 里对应的键立即失效;InvalidatesAcrossInstances 表示是否订阅到了,HybridCache 释放即退订。

何时使用 ​

  • 多实例部署,热点数据读多写少,可以接受最长 LocalTimeToLive 的旧值窗口(L2 是 Redis 时通常只在通知丢失时出现)。
  • 需要强一致的键直接用 ISharedCache,不经 L1。
  • 不需要 L1:不装配本包,ICache 由 Caching.InMemory 提供。

安装与启用 ​

bash
dotnet add package XiHan.Framework.Caching.Hybrid --prerelease

当前是预览版,安装要带 --prerelease;从源码或本地包源引用见快速上手。本包需要一个 caching.distributed 的提供者作 L2:

csharp
using XiHan.Framework.Caching.Hybrid;
using XiHan.Framework.Caching.Redis;
using XiHan.Framework.Hosting.AspNetCore;
using XiHan.Framework.Profiles.Standard;

var builder = WebApplication.CreateBuilder(args);

// Hybrid 顶替内存实现的 caching.local;L2 是 Redis
builder.AddXiHan(xihan => xihan
    .UseStandardProfile()
    .UseFeature<RedisCachingFeature>()
    .UseFeature<HybridCachingFeature>());

单实例开发环境不装配 Redis 时,L2 是 Caching.InMemory 的共享缓存,测试零外部基础设施。

配置 ​

键类型缺省说明
XiHan:Caching:Hybrid:LocalCapacity正整数10000L1 条目上限,满时淘汰最久未访问的条目
XiHan:Caching:Hybrid:LocalTimeToLiveSeconds正数30L1 存活时间上限,也是别的实例写入后最长的不可见窗口
json
{ "XiHan": { "Caching": { "Hybrid": { "LocalCapacity": 10000, "LocalTimeToLiveSeconds": 30 } } } }

非法值在装配时抛 ArgumentException。

主要类型 ​

类型说明
HybridCachingFeature特性入口;被选中提供 caching.local 时把 ICache 登记为 HybridCache
HybridCachingOptionsLocalCapacity、LocalTimeToLive;FromConfiguration 从配置节读取
HybridCache两级缓存,实现 ICache 与 IDisposable;InvalidatesAcrossInstances、LocalCount

使用示例 ​

启动后检查两级缓存是否订阅到了跨实例失效,没有时记一条警告,提醒 L1 的旧值窗口是 LocalTimeToLive:

csharp
using Microsoft.Extensions.Logging;
using XiHan.Framework.Caching.Abstractions.Caches;
using XiHan.Framework.Caching.Hybrid;

public sealed class HybridCacheCheck(ICache cache, ILogger<HybridCacheCheck> logger)
{
    public void Report()
    {
        if (cache is HybridCache hybrid && !hybrid.InvalidatesAcrossInstances)
        {
            logger.LogWarning("L2 has no change feed; L1 entries may be stale for up to the local time to live.");
        }
    }
}

读写本身与任何 ICache 相同,见缓存与分布式锁。

行为与限制 ​

  • L2 没有变更通知时(远程 Provider、内存实现),绕过本实例 HybridCache 的写入不会失效 L1,读到旧值的时间最长为 LocalTimeToLive;通知丢失时同样以此为上限。
  • L2 是内存实现时,经 ICacheInspector 删除的对象条目在 L1 里最长 LocalTimeToLive 后才消失(ADR-0058)。
  • L1 命中不触达 L2,因此不会延长 L2 里的滑动过期;滑动过期的条目经两级缓存读取时实际按绝对时间到期。
  • 同一个键被写成别的类型时,L1 按 T 读取当作未命中,交给 L2 判定。
  • L1 不实现键管理接口;管理界面列键用 L2 的 Store(ICacheStoreInspector)。

相关 ​

Released under The MIT License