尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

ASP.NET Boilerplate Per Request Redis Cache:将 Redis 查询按 HTTP 请求本地化缓存以消除性能瓶颈

发布时间:2026/9/23 17:02:27

资讯中心
01
ARTICLE

ASP.NET Boilerplate Per Request Redis Cache:将 Redis 查询按 HTTP 请求本地化缓存以消除性能瓶颈

ASP.NET Boilerplate Per Request Redis Cache:将 Redis 查询按 HTTP 请求本地化缓存以消除性能瓶颈
ASP.NET Boilerplate Per Request Redis Cache将 Redis 查询按 HTTP 请求本地化缓存以消除性能瓶颈【免费下载链接】aspnetboilerplateASP.NET Boilerplate - Web Application Framework项目地址: https://gitcode.com/gh_mirrors/as/aspnetboilerplate导读本文围绕 ASP.NET BoilerplateABP框架提供的Per Request Redis Cache每次请求 Redis 缓存机制展开它在默认 Redis 缓存ICacheManager Redis之上叠加了一层以HttpContext为作用域的本地缓存层使同一个 HTTP 请求内对同一 Redis 键的重复读取只真正访问 Redis 一次。读完本文你将掌握Abp.AspNetCore.PerRequestRedisCache包的接入方式、IAbpPerRequestRedisCacheManager与ICacheManager的关系、如何通过Configuration.Caching.UseRedis(usePerRequestRedisCache: true)一键替换全局缓存实现以及这套机制在源码层面AbpPerRequestRedisCache.cs究竟是如何工作的。一、问题背景Redis 查询同样存在性能成本ABP 的缓存抽象默认基于进程内MemoryCache实现详见 Caching.md。当你通过默认 Redis 缓存实现ICacheManager搭配 Redis获取缓存对象时每一次缓存读写操作都会真正发送到 Redis 服务器。虽然 Redis 通常比读取数据库、发起 API 调用这类操作快得多但 Redis 查询本身依然有性能开销且该开销随 Redis 服务器与业务服务器之间的网络距离延迟而波动。当一个项目在单个请求内对 Redis 发起大量查询时这些开销会被放大有时会引发性能问题甚至成为瓶颈。典型场景本地化Localization字典的反复读取原文档给出一个非常贴合实际的例子假设你正在做本地化缓存并把本地化键值字典保存在 Redis 中。每次页面加载的本地化流程可能需要几十次读取 Redis 来获取相关本地化键的值。如果不加处理每次页面加载都要向 Redis 发起几十次网络往返。如果使用 Per Request Redis Cache只需第一次从 Redis 拉取一次数据之后的查询全部在本地HttpContext内完成。适用前提瞬间变化不敏感的数据。这类非关键数据非常适合用本机制优化。二、核心概念IAbpPerRequestRedisCacheManagerIAbpPerRequestRedisCacheManager接口定义见 IAbpPerRequestRedisCacheManager.cs在用法上与ICacheManager完全一致返回的也是相同的数据类型。唯一的区别在于当缓存对象由IAbpPerRequestRedisCacheManager创建时Redis 查询会被缓存在HttpContext上。因此单个 HTTP 请求内对同一个键只会向 Redis 发送一次查询后续重复查询直接复用第一次的 Redis 响应结果从而提升性能。接口的源码注释对此有非常明确的界定值得反复阅读从IAbpPerRequestRedisCacheManager请求得到的ICache对象会按 HTTP 上下文存储 Redis 缓存并且在当前 HTTP 上下文存续期间不会再次从 Redis 拉取。不适用于数据在一个项目实例上发生变化、可能引起另一个项目实例出错的场景即跨实例强一致场景。只推荐用于可以确定缓存在当前 HTTP 请求结束前不会变化的值或者本身不重要的值例如某些 UI 视图设置。否则请继续使用ICacheManager。接口本身继承自ICacheManager因此对调用方而言它是透明的替换品。三、接入步骤三步启用 Per Request Redis Cache第 1 步引入 NuGet 包Abp.AspNetCore.PerRequestRedisCache该包在仓库中的对应源码目录为 src/Abp.AspNetCore.PerRequestRedisCache。第 2 步声明模块依赖在你的模块上添加对AbpAspNetCorePerRequestRedisCacheModule的DependsOn依赖[DependsOn(typeof(AbpAspNetCorePerRequestRedisCacheModule))] public class MyModule : AbpModule { //... }从源码看AbpAspNetCorePerRequestRedisCacheModule.cs该模块自身依赖AbpRedisCacheModule并在PreInitialize阶段将IAbpPerRequestRedisCacheManager注册为AbpPerRequestRedisCacheManager实现。也就是说Redis 相关基础能力是这个模块的硬依赖必须同时具备。第 3 步注入并使用IAbpPerRequestRedisCacheManagerpublic class TestAppService : ApplicationService { private readonly IAbpPerRequestRedisCacheManager _cacheManager; public TestAppService(IAbpPerRequestRedisCacheManager cacheManager) { _cacheManager cacheManager; } private ICache GetMyCache() _cacheManager.GetCache(MyCache); // get cache using IAbpPerRequestRedisCacheManager }拿到ICache之后Get / GetOrDefault / Set / Remove / Clear以及各自的 async 版本等所有缓存能力都可以直接使用。ABP 缓存抽象的具体用法ICacheManager、ICache、ITypedCache、过期时间配置、EntityCache等可参考 Caching.md。重要前提必须先启用 Redis要使用IAbpPerRequestRedisCacheManager必须启用 Redis参见 Caching.md 的 Redis 集成章节。因为 Per Request 层只是本地快照底层真正的数据源依然是 Redis——本机制优化的是读 Redis的频次而不是取代 Redis。四、一键替换UseRedis(usePerRequestRedisCache: true)如果你希望把项目当前使用的整个缓存实现替换为 Per Request Redis Cache而不改动任何业务代码只需在模块的PreInitialize中启用 Redis 时传入参数Configuration.Caching.UseRedis(usePerRequestRedisCache: true);这一行代码会将ICacheManager替换为IAbpPerRequestRedisCacheManager你的整个项目将开始以 Per Request 方式工作所有通过ICacheManager注入的代码无需修改。源码层面的替换机制从 AbpPerRequestRedisCacheExtensions.cs 可以看到UseRedis有两个重载public static void UseRedis(this ICachingConfiguration cachingConfiguration, bool usePerRequestRedisCache) public static void UseRedis(this ICachingConfiguration cachingConfiguration, ActionAbpRedisCacheOptions optionsAction, bool usePerRequestRedisCache)当usePerRequestRedisCache为true时扩展方法实际完成三件事注册替换实现IocManager.RegisterIfNotICacheManager, AbpPerRequestRedisCacheManagerForReplacement()。AbpPerRequestRedisCacheManagerForReplacement是 AbpPerRequestRedisCacheManager.cs 中定义的内部类继承自AbpPerRequestRedisCacheManager专门用于替换全局ICacheManager从而让所有依赖ICacheManager的代码自动获得 Per Request 行为。同步注册在线客户端存储IocManager.RegisterIfNotIOnlineClientStore, RedisOnlineClientStore()保证实时通信相关在线客户端数据也走 Redis。应用 Redis 选项通过optionsAction(iocManager.ResolveAbpRedisCacheOptions())应用配置连接字符串、DatabaseId 等。当usePerRequestRedisCache为false时则退化为普通的UseRedis(optionsAction)。注意替换语义是从此刻起项目中所有缓存都按每次请求本地化。这会影响所有被缓存数据的跨请求新鲜度语义务必先评估你的业务数据是否适合见第六节的适用边界。五、实现原理HttpContext.Items上的请求级缓存层Per Request Redis Cache 的真正实现类是AbpPerRequestRedisCacheAbpPerRequestRedisCache.cs它继承自标准的 Redis 缓存实现AbpRedisCacheAbpRedisCache.cs基于 StackExchange.Redis并实现了IAbpPerRequestRedisCache。在继承链上AbpPerRequestRedisCacheManager继承自CacheManagerBaseICacheCacheManagerBase.cs单例生命周期按缓存名管理ICache实例。5.1 本地缓存键的设计private const string AbpPerRequestRedisCachePrefix AbpPerRequestRedisCache:; protected virtual string GetPerRequestRedisCacheKey(string key) { return AbpPerRequestRedisCachePrefix NormalizeKey(key).ToString(); }本地缓存键由固定前缀AbpPerRequestRedisCache:加上经过标准化的 Redis 键组成。NormalizeKey是底层AbpRedisCache的能力会结合缓存名、多租户开关IMultiTenancyConfig对键进行统一规范化见 AbpRedisCache.cs因此多租户场景下不同租户的数据天然隔离不会互相污染。5.2 读取路径命中本地则不再访问 RedisTryGetValue的流程省略异常分支后如下通过注入的IHttpContextAccessor获取当前HttpContext若为 null例如后台任务、无 HTTP 上下文直接回退到base.TryGetValue走真正的 Redis。构造本地键检查httpContext.Items命中取出之前保存的ConditionalValueobject直接返回全程不碰 Redis。未命中调用base.TryGetValue真正访问 Redis然后把结果连同是否存在标记一起写入httpContext.Items供本请求后续使用。这里使用ConditionalValueobject而非普通对象是为了区分缓存了 null与从未缓存过——两者在httpContext.Items中的呈现必须不同否则无法判断是否真的需要回源 Redis。5.3 写入路径双写本地与 RedisSet/SetAsync在写入 Redis 成功后会把同样的值同步写入httpContext.Items键同样经过本地化处理。这意味着本请求内先写后读是自洽的写入后立刻读取不会再访问 Redis。5.4 失效路径Remove 与 Clear 同步清理本地快照Remove/RemoveAsync先删除 Redis 中的键再同步移除httpContext.Items中对应的本地条目。Clear调用base.Clear()清空 Redis 后遍历httpContext.Items删除所有以GetPerRequestRedisCacheKey()为前缀的本地条目。这样保证了失效操作在两个层面的一致性避免出现Redis 已删、本地仍返回旧值的脏读。5.5 批量操作与异常兜底提供批量版本TryGetValues/TryGetValuesAsync对缺失键一次性批量回源 Redis再整体回填HttpContext.Items、Set(pairs)/SetAsync(pairs)、Remove(keys)/RemoveAsync(keys)减少网络往返次数。所有读写方法都捕获ObjectDisposedException一旦出现例如请求结束、HttpContext被销毁后仍有访问会记录 Warn 日志并回退到 Redis 直接操作保证健壮性。5.6 生命周期与缓存名规则AbpPerRequestRedisCacheManager以单例注册继承CacheManagerBaseICache的ISingletonDependency在PreInitialize中通过IocManager.RegisterIfNotAbpPerRequestRedisCache(DependencyLifeStyle.Transient)注册具体缓存对象CreateCacheImplementation通过IocManager.ResolveAbpPerRequestRedisCache(new { name })按名创建。缓存名大小写敏感MyCache与MYCACHE是两个不同的缓存同一个名字在应用生命周期内共享同一缓存对象——只是其内容被本地化到每次请求。六、适用边界什么时候该用、什么时候不该用结合接口注释与实现Per Request Redis Cache 适合与不适合的场景可对比如下场景是否推荐原因本地化字典、UI 视图设置等读多写少、变化不敏感的数据推荐请求内首次读取后全部命中本地吞吐显著提升同一个键在单个请求内被反复读取几十次量级推荐从几十次 Redis 往返降为1 次 Redis 往返 N 次内存读取数据可能被其他实例/其他请求频繁更新且要求读取到最新值不推荐请求内复用旧快照可能导致跨实例不一致后台任务、Worker 等无HttpContext的执行环境自动回退源码中HttpContext null时直接走 Redis本地层不生效数据语义要求写入后其他实例立即可见不推荐必须等到当前请求结束本地快照才会失效一句话总结Per Request Redis Cache 是对每次请求内重复读 Redis的优化代价是牺牲请求内的数据新鲜度。只有当你确认缓存数据在本请求生命周期内不会变化或变化无伤大雅时才应使用它。七、配套 Redis 基础配置由于 Per Request 层构建在标准 Redis 缓存之上你需要先完成 Caching.md 中Redis Cache Integration的基础配置引入Abp.RedisCacheNuGet 包并在模块上声明DependsOn(typeof(AbpRedisCacheModule))。在PreInitialize中调用Configuration.Caching.UseRedis()。配置连接字符串与数据库 IDadd nameAbp.Redis.Cache connectionStringlocalhost/ add keyAbp.Redis.Cache.DatabaseId value2/ASP.NET Core 下可用委托方式覆盖Configuration.Caching.UseRedis(options { options.ConnectionString _appConfiguration[RedisCache:ConnectionString]; options.DatabaseId _appConfiguration.GetValueint(RedisCache:DatabaseId); });不同DatabaseId可用于在同一台服务器上隔离出不同的键空间。默认缓存过期时间为 60 分钟滑动过期可通过Configuration.Caching.ConfigureAll/Configure(MyCache, ...)按全局或按缓存名调整。所有这些配置对 Per Request 模式同样有效——本地快照层不改变缓存项的过期语义过期仍由 Redis 决定。八、总结Per Request Redis Cache 是 ABP 针对Redis 高频读取场景给出的低成本优化方案它不改动缓存抽象与业务代码仅通过一个实现类AbpPerRequestRedisCache在HttpContext.Items上维护请求级快照把单请求内对同一键的 N 次 Redis 查询收敛为1 次 Redis 查询 N-1 次内存命中同时通过键前缀规范化天然兼容多租户通过ObjectDisposedException兜底和HttpContext null回退保证在后台环境下的安全性。接入路径有两种按需选择局部使用引入Abp.AspNetCore.PerRequestRedisCache包 → 声明DependsOn(typeof(AbpAspNetCorePerRequestRedisCacheModule))→ 注入IAbpPerRequestRedisCacheManager获取缓存全局替换在PreInitialize调用Configuration.Caching.UseRedis(usePerRequestRedisCache: true)全项目自动切换到 Per Request 模式。核心取舍始终只有一个用请求内的数据新鲜度换取请求内 Redis 往返次数的大幅下降。把这条原则刻在脑子里就能在 ABP 项目中放心地用它解决 Redis 读密集场景的性能问题。【免费下载链接】aspnetboilerplateASP.NET Boilerplate - Web Application Framework项目地址: https://gitcode.com/gh_mirrors/as/aspnetboilerplate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。