如何在MemoryCache条目过期时调用函数?及未更新键告警实现
实现方案
核心思路
要实现这个需求,需要结合缓存过期配置、定时更新校验和键状态跟踪三个核心环节:
- 给缓存项设置120秒绝对过期时间,确保未被更新的项最终会自动失效
- 维护一个待监控的键集合,每次更新时标记已处理的键,对比找出未更新的键触发告警
- 每60秒执行一次更新+校验逻辑,形成闭环
具体实现步骤(以.NET MemoryCache 为例)
1. 初始化缓存与监控集合
用MemoryCache存储业务数据,同时用ConcurrentDictionary来跟踪所有需要监控的键(保证线程安全):
private readonly IMemoryCache _cache; // 存储需要监控的键,值为最后更新时间戳 private readonly ConcurrentDictionary<string, DateTime> _monitoredKeys = new(); public CacheMonitorService(IMemoryCache cache) { _cache = cache; // 初始化时可加载初始需要监控的键 // _monitoredKeys.TryAdd("key1", DateTime.UtcNow); }
2. 定义缓存项的过期策略
每次添加/更新缓存时,设置120秒的绝对过期时间,同时配置过期回调(双重保障,即使定时任务故障,过期时也能告警):
private void SetCacheItem(string key, object value) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromSeconds(120)) .RegisterPostEvictionCallback((evictedKey, _, reason, _) => { // 若因过期被移除,触发告警 if (reason == EvictionReason.Expired) { AlertAdmin($"缓存键 {evictedKey} 因未及时更新已过期"); } }); _cache.Set(key, value, cacheEntryOptions); }
3. 定时更新与校验逻辑
每60秒执行一次任务,流程如下:
- 获取本次要更新的键值对(比如从业务数据源拉取)
- 遍历更新每个键的缓存值,并重置过期时间
- 对比监控集合,找出未被本次更新覆盖的键,触发告警
- 更新监控集合为本次的键列表
public async Task UpdateAndCheckCacheAsync() { // 1. 获取本次需要更新的键值对(示例:从业务服务获取) var latestKeyValues = await GetLatestKeyValuesFromBusinessService(); var updatedKeys = latestKeyValues.Select(kv => kv.Key).ToHashSet(); // 2. 更新缓存并标记已更新 foreach (var kv in latestKeyValues) { SetCacheItem(kv.Key, kv.Value); _monitoredKeys.TryUpdate(kv.Key, DateTime.UtcNow, _monitoredKeys.GetValueOrDefault(kv.Key)); } // 3. 找出未被更新的键(监控集合中不在本次更新列表里的) var unupdatedKeys = _monitoredKeys.Keys.Except(updatedKeys).ToList(); foreach (var key in unupdatedKeys) { AlertAdmin($"缓存键 {key} 未在本次周期内更新"); // 从监控集合移除,避免重复告警(若后续恢复更新会重新加入) _monitoredKeys.TryRemove(key, out _); } // 4. 将本次新增的键加入监控集合 foreach (var key in updatedKeys.Except(_monitoredKeys.Keys)) { _monitoredKeys.TryAdd(key, DateTime.UtcNow); } }
4. 告警函数实现
根据实际需求实现告警逻辑(比如邮件、企业微信、日志告警等):
private void AlertAdmin(string message) { // 示例:写入告警日志,或调用通知接口 Console.WriteLine($"【缓存告警】{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} - {message}"); // 实际可替换为:SendEmailToAdmin(message); 或 SendWeChatAlert(message); }
5. 配置定时任务
用IHostedService来每60秒触发一次UpdateAndCheckCacheAsync:
public class CacheUpdateBackgroundService : BackgroundService { private readonly CacheMonitorService _cacheMonitor; private readonly TimeSpan _updateInterval = TimeSpan.FromSeconds(60); public CacheUpdateBackgroundService(CacheMonitorService cacheMonitor) { _cacheMonitor = cacheMonitor; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { await _cacheMonitor.UpdateAndCheckCacheAsync(); await Task.Delay(_updateInterval, stoppingToken); } } }
关键注意事项
- 线程安全:所有操作缓存和监控集合的地方必须保证线程安全,使用
ConcurrentDictionary和MemoryCache的线程安全方法 - 双重告警机制:定时校验+过期回调,避免因定时任务故障导致漏告警
- 避免重复告警:未更新的键告警后从监控集合移除,后续恢复更新时重新加入
内容的提问来源于stack exchange,提问作者S.M_Emamian
相关产品推荐
相关产品推荐

