XiHan Framework 处于预览阶段

查看获取方式
跳转到内容

XiHan.Framework.Caching.Abstractions ​

缓存子系统的抽象包:字节存储 ICacheStore、类型化缓存 ICache / ISharedCache、序列化器、分布式锁、Idempotency 阶段中间件与 IDistributedCache 适配都在这里,能力 caching.local@1 与 caching.distributed@1 由 Provider 提供。写缓存 Provider、在特性里用缓存或锁时引用它。

项内容
包 IDXiHan.Framework.Caching.Abstractions
环层Ring 2(抽象)
能力定义 caching.local@1、caching.distributed@1(CachingCapabilities),本包不提供
特性入口—
配置节XiHan:Caching:Idempotency(由提供 caching.distributed 的 Provider 读取)
依赖XiHan.Framework.Kernel、XiHan.Framework.Localization.Abstractions;Microsoft.Extensions.Caching.Abstractions、Microsoft.Extensions.DependencyInjection.Abstractions
AOT兼容;JsonCacheSerializer.CreateReflectionBased() 标了 RequiresUnreferencedCode / RequiresDynamicCode,AOT 下不可用
稳定性Stable;CacheStoreDistributedCache、DistributedCacheServiceCollectionExtensions、键管理与变更通知类型为 Preview

概述 ​

子系统只有一个存储契约 ICacheStore:读、写、不存在才写(TryAddAsync)、删、比较删(TryRemoveAsync)五个原子操作,加上批量的 GetManyAsync / SetManyAsync / RemoveManyAsync。Provider 只实现这一个接口,类型化缓存、分布式锁与幂等去重都由本包建立在它之上:

  • SerializingCache 把存储与 ICacheSerializer 组合成 ISharedCache,跨进程的 Provider(Redis、Remote)直接用它。
  • StoreDistributedLock 用 TryAddAsync 写入 lock:<resource> 与唯一令牌、用 TryRemoveAsync 按令牌释放,任何存储都能复用。
  • IdempotencyMiddleware 用 TryAddAsync 写「处理中」标记,用 SetAsync 写完成记录,用 TryRemoveAsync 清理。
  • CacheStoreDistributedCache 把 .NET 的 IDistributedCache 架在存储上。

本包另有两个可选接口给管理界面(ICacheStoreInspector、ICacheInspector)和一个给两级缓存的变更通知接口(ICacheChangeFeed),存储或缓存能做就实现,业务代码不依赖它们。

何时使用 ​

  • 特性要用缓存或锁:引用本包,按需在清单里声明 caching.local@1 或 caching.distributed@1,从容器取 ICache、ISharedCache 或 IDistributedLock。
  • 写新的缓存 Provider:实现 ICacheStore(可选再实现 ICacheStoreInspector、ICacheChangeFeed),用本包的 SerializingCache、StoreDistributedLock、IdempotencyMiddleware、AddXiHanDistributedCache 完成其余注册,并通过 Caching.Conformance 的 CacheStoreConformance。
  • 只需要一个能跑的缓存:直接用 Caching.InMemory(随 Profiles.Standard 装配),不必单独引用本包。

安装与启用 ​

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

当前是预览版,安装要带 --prerelease;从源码或本地包源引用见快速上手。本包没有特性入口,服务由 Provider 的特性注册。

配置 ​

幂等选项 IdempotencyOptions 由 Provider 在装配时用 IdempotencyOptions.FromConfiguration 读取:

键类型缺省说明
XiHan:Caching:Idempotency:TimeToLiveSeconds数字86400已完成调用结果的保留时间
XiHan:Caching:Idempotency:InProgressTimeToLiveSeconds数字60「处理中」标记的保留时间,即处理器崩溃且未能清理时同一个键被封锁的最长时间
json
{ "XiHan": { "Caching": { "Idempotency": { "TimeToLiveSeconds": 3600, "InProgressTimeToLiveSeconds": 30 } } } }

值按不变区域性解析,不是正数时抛 ArgumentException。

主要类型 ​

类型说明
ICacheStore字节级存储,Provider 实现;返回的字节不可修改
ICache类型化缓存:GetAsync<T>、SetAsync<T>、RemoveAsync、GetOrCreateAsync<T> 与批量方法;Store 暴露底层存储
ISharedCache跨进程共享的 ICache(caching.distributed)
SerializingCacheICacheStore + ICacheSerializer 组成的 ISharedCache
CacheKey键:非空、无空白与控制字符、不超过 MaxLength(512)、区分大小写;可从字符串隐式转换;IsValid、IsValidPrefix、Validate
CacheEntryOptions过期:Expires(ttl)、Sliding(window)、Default(Provider 默认存活时间),二选一,Validated() 校验组合
CacheEntryICacheStore.SetManyAsync 的一项:键、值、该项自己的过期选项
ICacheSerializer、JsonCacheSerializer、CacheSerializerRegistration值序列化;JsonCacheSerializer 要求选项带 TypeInfoResolver;TryAddJsonSerializer 供 Provider 按已有登记选序列化器
IDistributedLock、LockHandle、StoreDistributedLock、DistributedLockExtensions租约锁:TryAcquireAsync(resource, ttl) 返回 Maybe<LockHandle>,结果式的 AcquireAsync 被占用时返回 CACHING_CONFLICT_001;句柄 ReleaseAsync / DisposeAsync 释放
IdempotencyMiddleware、IdempotencyOptions、IdempotencyRecordIdempotency 阶段:按 Baggage xihan.idempotency-key 去重,回放结果与操作的响应(记录带 Operation、Response);WasReplayed(context) 判断结果是否来自回放
CacheStoreDistributedCache、DistributedCacheServiceCollectionExtensionsIDistributedCache 适配;AddXiHanDistributedCache(services, store) 幂等登记,已有登记不覆盖;Translate 换算过期选项
ICacheStoreInspector、CacheKeyQuery、CacheKeyPage、CacheEntryInfo可选的字节条目键管理:按前缀分页列键、读字节数与剩余存活、按前缀删除
ICacheInspector、CachedObjectInfo可选的对象条目键管理:进程内对象缓存的条目不在字节存储里,由它列出
ICacheChangeFeed、CacheChange可选的变更通知:本实例与其他实例对共享存储的写入与删除,用来失效本地副本
CachingCapabilities、CachingErrors、CachingMessages能力 ID 与版本、错误码、消息键与消息目录

使用示例 ​

在 ICacheStore 的原子原语上手写一个只允许一个调用方通过的门闩,适合需要按值判断的场景(锁已经封装了这一模式,优先用 IDistributedLock):

csharp
using System.Text;
using XiHan.Framework.Caching.Abstractions.Stores;

public sealed class OneShotGate(ICacheStore store)
{
    // 只有第一次写入成功的调用方拿到 true;ttl 到期后门闩重新打开
    public ValueTask<bool> TryEnterAsync(string name, string owner, TimeSpan ttl, CancellationToken cancellationToken)
        => store.TryAddAsync(new CacheKey($"gate:{name}"), Encoding.UTF8.GetBytes(owner), CacheEntryOptions.Expires(ttl), cancellationToken);

    // 只有当前持有者能提前打开门闩
    public ValueTask<bool> LeaveAsync(string name, string owner, CancellationToken cancellationToken)
        => store.TryRemoveAsync(new CacheKey($"gate:{name}"), Encoding.UTF8.GetBytes(owner), cancellationToken);
}

ICacheStore 由内存与 Redis Provider 登记在容器里;用远程 Provider 时从 ISharedCache.Store 取。缓存读写、批量、锁与幂等键的完整示例见缓存与分布式锁。

行为与限制 ​

  • 基础设施失败(存储不可达、超时)抛出携带 CACHING_UNAVAILABLE_001 的 FrameworkException,不伪装成未命中。
  • 批量方法一次往返、结果与入参顺序一一对应、空集合不触后端;SetManyAsync 任一项选项不合法时整批不写;RemoveManyAsync 同一个键出现多次第二次为 false。
  • GetOrCreateAsync 不合并并发:多个未命中的调用方各自执行工厂,后写覆盖先写。
  • LockHandle 只释放一次;锁到期后被他人获取会换成新令牌,原句柄释放返回 false。锁不可重入、不续约。
  • 幂等记录的作用域键是 idem:<租户>:<主体>:<键>,不满足 CacheKey 文法时以 CACHING_VALIDATION_001 结束调用;处理中返回 CACHING_CONFLICT_002;成功与失败的结果都会回放,操作调用回放时还原首次的响应(XGI 载荷、类型化对象、HTTP 状态码与正文),记录属于另一个调用或响应无法还原时返回 CACHING_CONFLICT_003(ADR-0064)。
  • CacheStoreDistributedCache:绝对过期两种写法取较早者,已过去的时刻抛 ArgumentOutOfRangeException;与滑动过期同时给时绝对优先;都不给时用 Provider 的默认存活时间(内存与 Redis 缺省 5 分钟);同步成员阻塞等待异步存储。
  • ICacheStoreInspector.RemoveByPrefixAsync 与 ICacheInspector.RemoveByPrefixAsync 拒绝空前缀;CacheKeyQuery.Limit 为 1 到 MaxLimit(1000),默认 100,越界不在构造时抛出,ListKeysAsync(返回 Result<CacheKeyPage>)以 CACHING_VALIDATION_003 返回、不访问后端(ADR-0084)。
  • ICacheChangeFeed 的处理器在写入的调用里同步执行,不应阻塞或抛出;本实例的订阅者同步收到通知,其他实例经后端转发、不回送给发出者;跨实例通知尽力而为,可能丢失。
错误码含义
CACHING_VALIDATION_001缓存键或幂等键不合法
CACHING_VALIDATION_002条目选项不合法(远程插件按操作集校验时返回,适配器原样传出;.NET 侧在调用插件之前抛 ArgumentException)
CACHING_VALIDATION_003键列表的每页上限越界(CacheKeyQuery.Limit 不在 1~1000,参数 limit、max;HTTP 400)
CACHING_UNAVAILABLE_001存储不可达或操作失败
CACHING_UNAVAILABLE_002内存缓存已达容量上限(健康项 Degraded 的原因)
CACHING_CONFLICT_001分布式锁被占用(AcquireAsync 返回;TryAcquireAsync 只返回无值)
CACHING_CONFLICT_002同一幂等键的调用仍在处理中
CACHING_CONFLICT_003幂等记录不能回放给本次调用:键被另一个调用用过,或记录里的响应无法还原

相关 ​

Released under The MIT License