Silky微服务框架在线文档Silky微服务框架在线文档
首页
文档
配置
源码解析
博文
github
gitee
首页
文档
配置
源码解析
博文
github
gitee
  • 启动时

    • 主机的构建
    • 服务引擎
    • 模块
    • 服务与服务条目的解析
    • Silky服务主机
    • 依赖注入约定
    • RPC 服务代理
  • 运行时

    • 终结点与路由
    • 执行器与调度
    • 本地执行器与服务端过滤器
    • 远程执行器与 RPC 调用
    • RPC 服务端消息处理
    • 服务治理
    • 缓存拦截器
    • 分布式事务(TCC)
    • HTTP 网关请求管道
    • 过滤器执行管道
    • Polly 弹性策略管道
    • 端点健康监控

概述

silky 的缓存拦截器(Caching Interceptor)提供了一种基于 AOP 的透明缓存机制:开发者只需在服务接口方法上标注特定特性,框架即可在方法调用时自动完成缓存的读取、更新和删除,无需在业务代码中编写缓存逻辑。

缓存拦截器底层依赖 IDistributedInterceptCache(默认基于 Redis 实现),通过 CachingInterceptor(实现了 SilkyInterceptor)拦截服务方法调用并完成缓存操作。


启用条件

缓存拦截器由以下两个条件共同控制:

  1. 服务治理选项中 EnableCachingInterceptor = true(默认值为 true)
  2. 服务方法上至少标注了一个缓存拦截特性
# appsettings.yml
Governance:
  EnableCachingInterceptor: true

如果某个方法不希望缓存拦截器生效,可以在该方法的 [Governance] 特性中单独关闭:

[Governance(EnableCachingInterceptor = false)]
Task<AccountDto> GetSensitiveDataAsync(long id);

缓存拦截特性

silky 提供了四种缓存拦截特性,分别对应不同的缓存操作:

GetCachingIntercept(读取缓存)

[GetCachingIntercept("{id}")]
Task<AccountDto> GetByIdAsync(long id);

行为:先查询缓存,命中则直接返回缓存值,不执行方法;未命中则执行方法,并将结果写入缓存。

属性类型默认值说明
KeyTemplate(构造参数)string—缓存键模板,使用 {参数名} 占位符引用方法参数
OnlyCurrentUserDataboolfalse为 true 时,缓存键自动追加当前用户标识,实现用户级数据隔离
IgnoreMultiTenancyboolfalse为 true 时,忽略多租户隔离,不同租户共享同一缓存

UpdateCachingIntercept(更新缓存)

[UpdateCachingIntercept("{id}")]
Task<AccountDto> UpdateAsync(long id, UpdateAccountInput input);

行为:执行方法后,直接将新的返回値覆写到缓存中(SetAsync)。若缓存键中有参数値为 null 且 IgnoreWhenCacheKeyNull = true,则改为删除该缓存条目。支持在同一方法上标注多个 [UpdateCachingIntercept](AllowMultiple = true)。

属性类型默认值说明
KeyTemplate(构造参数)string—缓存键模板
OnlyCurrentUserDataboolfalse用户级数据隔离
IgnoreMultiTenancyboolfalse多租户隔离控制
IgnoreWhenCacheKeyNullbooltrue为 true 时,若缓存键解析结果为空则忽略该缓存操作;为 false 则抛出异常

RemoveCachingIntercept(删除缓存)

// 通过 CacheName 字符串指定缓存分区
[RemoveCachingIntercept("Account", "{id}")]
Task DeleteAsync(long id);

// 通过类型自动推断 CacheName(取类型的短名)
[RemoveCachingIntercept(typeof(GetCachingInterceptAttribute), "{id}")]
Task DeleteAsync(long id);

行为:执行方法后,根据指定的 CacheName 和 KeyTemplate 删除对应的缓存条目。支持多个 [RemoveCachingIntercept]。

属性类型默认值说明
CacheName(构造参数)string—缓存分区名称(类似 Redis Hash 的 Hash Key)
KeyTemplate(构造参数)string—缓存键模板
OnlyCurrentUserDataboolfalse用户级数据隔离
IgnoreMultiTenancyboolfalse多租户隔离控制

RemoveMatchKeyCachingIntercept(模式匹配删除缓存)

// 删除所有以 "Account:" 开头的缓存键
[RemoveMatchKeyCachingIntercept("Account", "Account:*")]
Task BatchUpdateAsync(BatchUpdateInput input);

行为:使用正则或通配符模式匹配缓存键,批量删除匹配的缓存条目。适用于批量操作后清理相关缓存。


缓存键(KeyTemplate)解析

缓存键通过 CacheKeyHelper.GetCachingInterceptKey() 解析生成,支持两种模式:

参数名占位符

使用 {参数名} 引用方法参数:

// 方法签名:Task<AccountDto> GetByIdAsync(long id)
// KeyTemplate: "{id}"
// 生成的缓存键:Account:123(CacheName:实际值)

[HashKey] 属性占位符

当参数是复杂对象时,使用 [HashKey] 标记对象属性:

public class QueryInput
{
    [HashKey]
    public long Id { get; set; }
    public string Name { get; set; }
}

// KeyTemplate: "{queryInput}"
// 实际取 queryInput.Id 的值作为缓存键

框架内部通过 CacheKeyType 区分这两种情况:

CacheKeyType含义
Parameter直接使用参数值(或其序列化结果)作为 Key
Attribute读取对象中被 [HashKey] 标注的属性作为 Key

缓存名(CacheName)

缓存名(CacheName)类似命名空间,将同类数据的缓存条目组织在一起,避免键冲突:

  • GetCachingInterceptAttribute 和 UpdateCachingInterceptAttribute 的 CacheName 由框架根据接口类型自动推断(通常为接口短名,例如 IAccountAppService → Account)
  • RemoveCachingInterceptAttribute 需要显式指定 CacheName,以便跨方法清理同一分区的缓存

多租户隔离

当应用启用了多租户时,缓存拦截器默认在缓存键中追加当前租户 ID,实现租户间的缓存隔离:

缓存键格式:{CacheName}:{TenantId}:{UserId}:{KeyValue}
  • IgnoreMultiTenancy = true:不追加租户 ID,不同租户共享同一缓存(适用于公共数据)
  • OnlyCurrentUserData = true:追加当前用户 ID,实现用户级隔离(适用于个人数据)

分布式事务场景中的缓存

当服务方法同时开启了缓存拦截和分布式事务时,CachingInterceptor 会跳过 Get 缓存逻辑(不从缓存读取),直接执行方法,以确保事务中读取到的是最新的数据库数据:

// CachingInterceptor 内部逻辑
if (serviceEntryDescriptor.IsDistributeTransaction)
{
    // 分布式事务中跳过读缓存,直接执行方法
    await InvocationProceedAsync(invocation);
}
else
{
    // 非分布式事务:正常走缓存读取逻辑
    invocation.ReturnValue = await GetResultFirstFromCache(cacheName, cacheKey);
}

执行流程

调用方(HTTP 请求 / RPC 调用)
    │
    ▼
CachingInterceptor.InterceptAsync()
    │
    ├── GovernanceOptions.EnableCachingInterceptor == false 
    │       → 直接执行方法(bypass)
    │
    ├── 存在 [GetCachingIntercept]
    │       │
    │       ├── 查询缓存(_distributedCache.GetOrAddAsync)
    │       │       │
    │       │       ├── 命中:直接返回缓存值,不执行方法
    │       │       └── 未命中:执行方法 + 写入缓存
    │       │
    ├── 存在 [UpdateCachingIntercept](可多个)
    │       │
    │       └── 执行方法后:删除旧缓存 + SetAsync 写入新值
    │
    └── 存在 [RemoveCachingIntercept] / [RemoveMatchKeyCachingIntercept](可多个)
            │
            └── 执行方法后:RemoveForInterceptAsync 删除匹配缓存

使用示例

以账户服务为例:

/// <summary>
/// 查询:读取缓存,命中则跳过方法执行
/// </summary>
[GetCachingIntercept("{id}")]
Task<AccountDto> GetByIdAsync(long id);

/// <summary>
/// 更新:执行方法后更新缓存
/// </summary>
[UpdateCachingIntercept("{id}")]
Task<AccountDto> UpdateAsync(long id, UpdateAccountInput input);

/// <summary>
/// 删除:执行方法后清理对应缓存
/// </summary>
[RemoveCachingIntercept("Account", "{id}")]
Task DeleteAsync(long id);

/// <summary>
/// 多缓存联动:删除主数据缓存 + 列表缓存
/// </summary>
[RemoveCachingIntercept("Account", "{input.Id}")]
[RemoveCachingIntercept("AccountList", "*")]
Task<AccountDto> UpdateStatusAsync(UpdateStatusInput input);

与 IDistributedInterceptCache

IDistributedInterceptCache 是缓存拦截器使用的缓存接口,其默认实现基于 IDistributedCache(Redis):

方法说明
GetOrAddAsync(key, factory)读取缓存,未命中时调用 factory 写入
SetAsync(key, value)写入缓存
RemoveForInterceptAsync(key, cacheName, isMatchKey)删除缓存,isMatchKey=true 时按模式批量删除
UpdateCacheName(name)设置当前操作的缓存分区
SetIgnoreMultiTenancy(ignore)控制当前操作是否忽略多租户隔离

缓存拦截器在 Redis 中的存储结构:

  • Key 格式:{AppPrefix}:{CacheName}:{TenantId}:{ExtraKey}
  • Value:方法返回值的 JSON 序列化结果
编辑当前页
Prev
服务治理
Next
分布式事务(TCC)