CacheManager - 每 x 分钟或到期时刷新缓存
CacheManager - Refresh cache every x minutes or on expiration
使用 CacheManager,您如何最好地实现这种情况,其中缓存实例包含数据,可能需要很长时间才能从慢速源获取?
我从不希望用户等待缓存填充(我不关心第一次加载)
我可以想到两种方法,但不确定是否可以使用 CacheManager:
“预定”刷新
- 设置缓存实例在 60 分钟后过期
- 每 15 分钟,安排一些事情来刷新缓存实例
到期时刷新
- 当缓存实例过期时,触发刷新数据的事件。当数据刷新时(或者如果刷新失败),缓存实例仍然returns“陈旧”数据。
Cachemanager 在技术上有哪些可能,哪种方法最有效 - 如果有的话?
我没听懂你的"Scheduled"刷新思路,为什么设置缓存60分钟过期,每15分钟刷新一次?
如果我是你,我会这样做(有点像你的第二个想法):
您第一次填充 CacheManager,然后每 60 分钟创建一个新的 CacheManager 实例(并填充它),如果一切顺利,您替换旧实例(您也处置它)。
我想到这个是因为在 CacheManager 中,过期的项目不会被自动删除,所以它们仍然存在,如果你决定做类似 [Populate-ClearAll-Populate-..] 的事情,那么你可能会得到遇到一些问题,所以最安全的方法是在每次刷新时创建一个新实例,以确保您的缓存始终有效。
关于您的第二个选项 "Refresh on expiry",这不起作用,因为如果项目过期,CacheManager 不会 return 缓存值。而且,当你 "refresh" 缓存时,你最终会从其他调用者那里得到很多缓存未命中。
我的建议:
如果您只有少量的键被缓存了 60 分钟并且它们全部 "work the same",只需启动一个后台任务,该任务与您的进程异步运行并每 15 分钟刷新一次缓存。
如果您有许多密钥,这些密钥在过期方面可能会有很大差异,您可以缓存 60 分钟过期的数据,并存储一个 15 分钟过期的辅助密钥(没有实际值的假密钥)。然后,如果假密钥过期,刷新实际 key/value...
对于假的key
,例如使用前缀+实际密钥,然后监听OnRemove
事件。
快速示例程序
internal class Program
{
private static ICacheManager<object> _cache;
private static void Main(string[] args)
{
_cache = CacheFactory.Build(c => c.WithDictionaryHandle());
_cache.OnRemoveByHandle += Cache_OnRemoveByHandle;
for (var i = 0; i < 10; i++)
{
_cache.Add("key" + i, "data" + i);
AddFakeKey("key" + i);
Thread.Sleep(1000);
}
Console.ReadKey();
}
private static void AddFakeKey(string forKey)
{
_cache.Add(new CacheItem<object>("trigger_" + forKey, "n/a", ExpirationMode.Absolute, TimeSpan.FromSeconds(1)));
}
private static void Cache_OnRemoveByHandle(object sender, CacheItemRemovedEventArgs e)
{
// Remark: you might get this event triggered for each level of the cache e.Level can be checked to react only on the lowest level...
Console.WriteLine("OnRemoveByHandle " + e.ToString());
if (e.Key.StartsWith("trigger_") && e.Reason == CacheItemRemovedReason.Expired)
{
var key = e.Key.Substring(8);
Console.WriteLine("Updating key " + key);
// updating the key
_cache.Update(key, _ => "new value");
// add a new fake key for another round
AddFakeKey(key);
}
}
}
如果启用了键空间通知,这甚至可以与 Redis 一起使用。
使用 CacheManager,您如何最好地实现这种情况,其中缓存实例包含数据,可能需要很长时间才能从慢速源获取?
我从不希望用户等待缓存填充(我不关心第一次加载)
我可以想到两种方法,但不确定是否可以使用 CacheManager:
“预定”刷新
- 设置缓存实例在 60 分钟后过期
- 每 15 分钟,安排一些事情来刷新缓存实例
到期时刷新
- 当缓存实例过期时,触发刷新数据的事件。当数据刷新时(或者如果刷新失败),缓存实例仍然returns“陈旧”数据。
Cachemanager 在技术上有哪些可能,哪种方法最有效 - 如果有的话?
我没听懂你的"Scheduled"刷新思路,为什么设置缓存60分钟过期,每15分钟刷新一次?
如果我是你,我会这样做(有点像你的第二个想法):
您第一次填充 CacheManager,然后每 60 分钟创建一个新的 CacheManager 实例(并填充它),如果一切顺利,您替换旧实例(您也处置它)。
我想到这个是因为在 CacheManager 中,过期的项目不会被自动删除,所以它们仍然存在,如果你决定做类似 [Populate-ClearAll-Populate-..] 的事情,那么你可能会得到遇到一些问题,所以最安全的方法是在每次刷新时创建一个新实例,以确保您的缓存始终有效。
关于您的第二个选项 "Refresh on expiry",这不起作用,因为如果项目过期,CacheManager 不会 return 缓存值。而且,当你 "refresh" 缓存时,你最终会从其他调用者那里得到很多缓存未命中。
我的建议:
如果您只有少量的键被缓存了 60 分钟并且它们全部 "work the same",只需启动一个后台任务,该任务与您的进程异步运行并每 15 分钟刷新一次缓存。
如果您有许多密钥,这些密钥在过期方面可能会有很大差异,您可以缓存 60 分钟过期的数据,并存储一个 15 分钟过期的辅助密钥(没有实际值的假密钥)。然后,如果假密钥过期,刷新实际 key/value...
对于假的key
,例如使用前缀+实际密钥,然后监听OnRemove
事件。
快速示例程序
internal class Program
{
private static ICacheManager<object> _cache;
private static void Main(string[] args)
{
_cache = CacheFactory.Build(c => c.WithDictionaryHandle());
_cache.OnRemoveByHandle += Cache_OnRemoveByHandle;
for (var i = 0; i < 10; i++)
{
_cache.Add("key" + i, "data" + i);
AddFakeKey("key" + i);
Thread.Sleep(1000);
}
Console.ReadKey();
}
private static void AddFakeKey(string forKey)
{
_cache.Add(new CacheItem<object>("trigger_" + forKey, "n/a", ExpirationMode.Absolute, TimeSpan.FromSeconds(1)));
}
private static void Cache_OnRemoveByHandle(object sender, CacheItemRemovedEventArgs e)
{
// Remark: you might get this event triggered for each level of the cache e.Level can be checked to react only on the lowest level...
Console.WriteLine("OnRemoveByHandle " + e.ToString());
if (e.Key.StartsWith("trigger_") && e.Reason == CacheItemRemovedReason.Expired)
{
var key = e.Key.Substring(8);
Console.WriteLine("Updating key " + key);
// updating the key
_cache.Update(key, _ => "new value");
// add a new fake key for another round
AddFakeKey(key);
}
}
}
如果启用了键空间通知,这甚至可以与 Redis 一起使用。