ASP.NET Core 5 N-tier应用无需改动原有架构新增缓存层方案咨询
缓存层部署位置与架构设计方案
缓存层部署位置
缓存层放在业务层与数据库层之间是最优选择,完全不需要改动原有N层架构的核心结构、各层接口契约和业务逻辑代码。
调整后的调用链路为:
API layer
||
Business Layer
||
Cache Layer
||
Database Layer
缓存层对上游业务层完全透明,业务层无需感知缓存的存在,仅当缓存未命中时才会向下请求数据库层,查询完成后自动回写缓存再返回给上层。
无侵入实现方案
采用装饰器模式实现缓存层即可做到完全不改动原有架构逻辑:
- 缓存层实现和原有数据库层完全一致的仓储/数据访问接口
- 原有数据库层的实现作为缓存层的内部依赖传入
- 依赖注入时仅修改注册逻辑,上层业务层的接口依赖无需任何调整
具体实现建议
- 统一缓存键生成规则,推荐用
{实体类型}:{参数特征值}的格式,比如Product:123,避免不同业务的缓存键冲突 - 极少变更的数据可设置长周期的绝对过期时间,同时提供手动淘汰缓存的入口,数据变更时主动删除对应缓存即可避免脏数据
- 本地部署场景直接用ASP.NET Core自带的
IMemoryCache做内存缓存,分布式部署场景仅需修改缓存层的实现切换为分布式缓存,上层代码完全不需要调整 - 缓存穿透、击穿、雪崩的常规防护要做:不存在的查询请求缓存空值,热点key加互斥锁,过期时间设置随机偏移量避免同时失效
实现示例
原有数据层代码(无需修改)
// 原有数据访问接口 public interface IProductRepository { Task<Product> GetByIdAsync(int id); } // 原有数据库实现 public class ProductRepository : IProductRepository { private readonly AppDbContext _dbContext; public ProductRepository(AppDbContext dbContext) => _dbContext = dbContext; public async Task<Product> GetByIdAsync(int id) { return await _dbContext.Products.FindAsync(id); } }
新增缓存层代码
// 缓存装饰器,实现相同的数据访问接口 public class CachedProductRepository : IProductRepository { private readonly IProductRepository _innerDatabaseRepo; private readonly IMemoryCache _cache; private const int CacheExpirationMinutes = 120; public CachedProductRepository(IProductRepository innerDatabaseRepo, IMemoryCache cache) { _innerDatabaseRepo = innerDatabaseRepo; _cache = cache; } public async Task<Product> GetByIdAsync(int id) { var cacheKey = $"Product:{id}"; return await _cache.GetOrCreateAsync(cacheKey, async entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheExpirationMinutes); return await _innerDatabaseRepo.GetByIdAsync(id); }); } }
依赖注入调整(仅需修改这一处注册逻辑)
// 原有注册逻辑注释即可 // services.AddScoped<IProductRepository, ProductRepository>(); // 新的注册逻辑 services.AddMemoryCache(); services.AddScoped<ProductRepository>(); services.AddScoped<IProductRepository, CachedProductRepository>(sp => { return new CachedProductRepository( sp.GetRequiredService<ProductRepository>(), sp.GetRequiredService<IMemoryCache>() ); });
内容的提问来源于stack exchange,提问作者Kerollos Melad
相关产品推荐
相关产品推荐

