如何在ASP.NET Core中实现自动刷新的高效本地缓存系统?
在ASP.NET Core中实现带自动刷新的本地缓存方案
核心方案
利用ASP.NET Core原生的IMemoryCache存储缓存数据,搭配后台定时任务(基于BackgroundService)定期刷新缓存。刷新时遵循「先获取新数据,再替换旧缓存」的原则,同时用锁机制避免重复触发刷新操作,确保请求始终能拿到有效数据,不会出现空窗期。
具体实现步骤
1. 定义数据获取接口(模拟外部数据源)
先封装外部数据的获取逻辑,方便后续替换和测试:
public interface IDataProvider { // 模拟从API/数据库获取最新数据 Task<MyDataModel> GetLatestDataAsync(); } public class DataProvider : IDataProvider { public async Task<MyDataModel> GetLatestDataAsync() { // 这里替换为实际的外部数据源调用逻辑 await Task.Delay(100); // 模拟IO耗时 return new MyDataModel { Id = Guid.NewGuid(), Content = $"最新数据:{DateTime.Now:yyyy-MM-dd HH:mm:ss}", LastUpdated = DateTime.Now }; } } // 数据模型示例 public class MyDataModel { public Guid Id { get; set; } public string Content { get; set; } public DateTime LastUpdated { get; set; } }
2. 实现后台缓存刷新服务
创建一个后台服务,负责定时刷新缓存,同时用SemaphoreSlim防止并发刷新:
public class CacheRefreshService : BackgroundService { private readonly IMemoryCache _cache; private readonly IDataProvider _dataProvider; private readonly SemaphoreSlim _refreshLock = new SemaphoreSlim(1, 1); private readonly TimeSpan _refreshInterval = TimeSpan.FromMinutes(5); // 5分钟刷新一次 public CacheRefreshService(IMemoryCache cache, IDataProvider dataProvider) { _cache = cache; _dataProvider = dataProvider; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 启动时先初始化缓存 await RefreshCacheAsync(); while (!stoppingToken.IsCancellationRequested) { await Task.Delay(_refreshInterval, stoppingToken); await RefreshCacheAsync(); } } private async Task RefreshCacheAsync() { // 确保同一时间只有一个刷新任务执行 if (!await _refreshLock.WaitAsync(TimeSpan.FromSeconds(5))) { return; // 如果5秒内拿不到锁,放弃本次刷新,避免阻塞 } try { // 先获取最新数据,再替换缓存——避免缓存空窗期 var latestData = await _dataProvider.GetLatestDataAsync(); _cache.Set("CachedData:Latest", latestData); } finally { _refreshLock.Release(); } } }
3. 注册服务到DI容器
在Program.cs中注册数据提供器和后台刷新服务:
var builder = WebApplication.CreateBuilder(args); // 注册数据提供器 builder.Services.AddScoped<IDataProvider, DataProvider>(); // 注册后台缓存刷新服务 builder.Services.AddHostedService<CacheRefreshService>(); // MemoryCache默认已注册,无需额外添加 // 添加控制器 builder.Services.AddControllers(); var app = builder.Build(); app.MapControllers(); app.Run();
4. 在业务逻辑中使用缓存
在控制器或服务中直接从IMemoryCache获取数据,因为后台服务已经维护了最新的缓存:
[ApiController] [Route("api/[controller]")] public class DataController : ControllerBase { private readonly IMemoryCache _cache; public DataController(IMemoryCache cache) { _cache = cache; } [HttpGet] public IActionResult GetCachedData() { if (_cache.TryGetValue("CachedData:Latest", out MyDataModel data)) { return Ok(data); } // 理论上不会走到这里,因为启动时已经初始化了缓存 return StatusCode(StatusCodes.Status503ServiceUnavailable, "缓存未初始化"); } }
关键细节说明
- 避免缓存空窗期:刷新时先获取新数据,再覆盖旧缓存,确保请求始终能拿到有效数据,不会出现缓存失效后等待新数据的情况。
- 防止并发刷新:用
SemaphoreSlim控制刷新操作,避免多个刷新任务同时执行,减少对外部数据源的压力。 - 初始化缓存:后台服务启动时立即执行一次刷新,确保缓存一开始就有数据,避免首次请求无缓存可用。
- 可配置性:可以把刷新间隔放到配置文件(如
appsettings.json)中,通过IOptions<T>读取,方便调整。
内容的提问来源于stack exchange,提问作者ياسمين يعقوبي
相关产品推荐
相关产品推荐

