一个对.NET平台下缓存解决方案的探索关于:
Add方法会在已存在相同Key的缓存时返回false,所以不会重复添加。
MemoryCache.Default.Add(Warehouse.CACHE_KEY, Warehouse.Warehouses, DateTimeOffset.Now.AddSeconds(2));
var warehousesFromCache = MemoryCache.Default.Get(Warehouse.CACHE_KEY) as List;
Assert.That(warehousesFromCache, Is.Not.Null);
Assert.That(warehousesFromCache.Count, Is.EqualTo(2));
返回新添加或已存在相同Key的缓存Value。
MemoryCache.Default.Add(Warehouse.CACHE_KEY, Warehouse.Warehouses, MemoryCache.InfiniteAbsoluteExpiration);
var caches = MemoryCache.Default.AddOrGetExisting(Warehouse.CACHE_KEY, Warehouse.Warehouses.Where(o => o.WarehouseNumber == "07").ToList(), MemoryCache.InfiniteAbsoluteExpiration) as List;
Assert.That(caches.Count, Is.Not.EqualTo(1));
Assert.That(caches.Count, Is.EqualTo(2));
更新(替换)缓存。
MemoryCache.Default.Add(Warehouse.CACHE_KEY, Warehouse.Warehouses, MemoryCache.InfiniteAbsoluteExpiration);
var specialWarehouse = Warehouse.Warehouses.Where(o => o.WarehouseNumber == "07").ToList();
MemoryCache.Default.Set(Warehouse.CACHE_KEY, specialWarehouse, MemoryCache.InfiniteAbsoluteExpiration);
var caches = MemoryCache.Default.Get(Warehouse.CACHE_KEY) as List;
Assert.That(caches, Is.Not.Null);
Assert.That(caches.Count, Is.EqualTo(1));
Assert.That(caches[0].WarehouseNumber == "07");
有两种缓存过期策略:
MemoryCache.Default.Add(Warehouse.CACHE_KEY+"1", Warehouse.Warehouses, DateTimeOffset.Now.AddSeconds(2));
Thread.Sleep(2000);
var warehousesFromCache = MemoryCache.Default.Get(Warehouse.CACHE_KEY+"1") as List;
Assert.That(warehousesFromCache, Is.Null);
当Cache丢失前,MemoryCache会回掉一个Callback委托进行通知,此时,为了让应用程序没有该缓存真空期,我们应该立即刷新缓存,保持最新的同时防止客户端出现缓存未命中(缓存丢失)的现象发生。
[Test]
public void CacheShouldRefreshSuccessAfterExpired()
{
MemoryCache.Default.Set(Warehouse.CACHE_KEY, Warehouse.Warehouses, new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2),
UpdateCallback = this.CacheRefreshCallback
});
Thread.Sleep(5000);
var warehousesFromCache = MemoryCache.Default.Get(Warehouse.CACHE_KEY) as List;
Assert.That(warehousesFromCache, Is.Not.Null);
}
private void CacheRefreshCallback(CacheEntryUpdateArguments args)
{
var cacheItem = MemoryCache.Default.GetCacheItem(args.Key);
var cacheObj = cacheItem.Value;
cacheItem.Value = cacheObj;
args.UpdatedCacheItem = cacheItem;
var policy = new CacheItemPolicy
{
UpdateCallback = new CacheEntryUpdateCallback(CacheRefreshCallback),
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2)
};
args.UpdatedCacheItemPolicy = policy;
}
在需要缓存文件内容时适用;且当文件内容更改时自动刷新缓存。
var configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "warehouse.txt");
var contents = MemoryCache.Default.Get("WarehouseConfig");
if (contents == null)
{
var policy = new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(30),
};
policy.ChangeMonitors.Add(new HostFileChangeMonitor(new List() { configFilePath }));
MemoryCache.Default.Set("WarehouseConfig", File.ReadAllText(configFilePath), policy);
}
Thread.Sleep(5000);
var contentsFromCache = MemoryCache.Default.Get("WarehouseConfig");
Assert.That(contentsFromCache, Is.Not.Null);
Assert.That(contentsFromCache, Is.EqualTo("01,02,03"));
CacheManager是GitHub上一个开源的专注于.NET Cache领域项目,其有许多新的思想和概念是.NET MemoryCache所不具备的;相对于MemoryCache,其有以下特有feature:
添加缓存;已存在时返回false。
var cacheManager = CacheFactory.Build>(setting =>
{
setting.WithDictionaryHandle();
setting.WithSystemRuntimeCacheHandle();
});
cacheManager.Add(Warehouse.CACHE_KEY, Warehouse.Warehouses);
var caches = cacheManager.Get(Warehouse.CACHE_KEY);
Assert.That(caches.Count, Is.EqualTo(2));
Assert.That(cacheManager.CacheHandles.Count, Is.EqualTo(2));
返回新添加的或已存在相同Key的缓存。
var cacheManager = CacheFactory.Build>(settings =>
{
settings.WithDictionaryHandle();
});
var caches = cacheManager.GetOrAdd(Warehouse.CACHE_KEY, Warehouse.Warehouses);
Assert.That(caches.Count, Is.EqualTo(2));
不存在就添加,存在就更新。
var manager = CacheFactory.Build>(settings =>
{
settings.WithDictionaryHandle();
});
manager.Add(Company.CACHE_KEY, Company.MovieCompanies);
manager.AddOrUpdate(Company.CACHE_KEY, Company.ITCompanies, o => Company.ITCompanies);
var caches = manager.Get(Company.CACHE_KEY);
Assert.That(caches, Is.Not.Null);
Assert.That(caches.Count, Is.EqualTo(2));
Assert.That(caches[0].CompanyType, Is.EqualTo(CompanyType.ITCompany));
更新缓存。
var manager = CacheFactory.Build(settings =>
{
settings.WithDictionaryHandle();
});
manager.Add(Company.CACHE_KEY, Company.MovieCompanies);
manager.Update(Company.CACHE_KEY, o => Company.ITCompanies.Union(o as List).ToList());
var caches = manager.Get>(Company.CACHE_KEY);
Assert.That(caches.Count, Is.EqualTo(4));
从配置文件加载缓存策略等信息。
var cfg = ConfigurationBuilder.LoadConfigurationFile("Caches.config", "ITCompaniesCacheConfig");
var manager = CacheFactory.FromConfiguration>(cfg);
manager.Add(Company.CACHE_KEY, Company.ITCompanies);
var caches = manager.Get(Company.CACHE_KEY);
Assert.That(caches, Is.Not.Null);
Assert.That(caches.Count, Is.EqualTo(2));
将同一份数据缓存到多个Layer,会按Layer(Handle)的添加顺序去命中缓存(返回第一个命中的)。
var manager = CacheFactory.Build>(settings =>
{
settings.WithDictionaryHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromSeconds(1));
settings.WithSystemRuntimeCacheHandle();
settings.WithUpdateMode(CacheUpdateMode.None);
});
// all cache layer hold the ITCompanies.
manager.Add(Company.CACHE_KEY, Company.ITCompanies);
Thread.Sleep(1000);
var caches = manager.Get(Company.CACHE_KEY); // ONLY When GET: If find some layer was difference from others, the "Update" strategy will be executed.
Assert.That(caches, Is.Not.Null);
Assert.That(caches.Count, Is.EqualTo(2));
Assert.That(manager.CacheHandles.Count, Is.EqualTo(2));
Assert.That(manager.CacheHandles.First().Count, Is.EqualTo(0));
Assert.That(manager.CacheHandles.Last().Count, Is.GreaterThanOrEqualTo(1));
缓存过期是针对每个Layer的,可以用连续调用的方式来初始化一个带有过期策略的CacheManager。
var manager = CacheFactory.Build>(settings =>
{
settings.WithDictionaryHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromSeconds(1));
settings.WithSystemRuntimeCacheHandle();
settings.WithUpdateMode(CacheUpdateMode.None);
});
注意这里的WithUpdateMode,CacheUpdateMode有三种值,分别代表:
Cache在各种Operation下的次数统计信息输出。
var manager = CacheFactory.Build>(settings =>
{
settings.WithDictionaryHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromSeconds(1)).EnableStatistics().EnablePerformanceCounters();
settings.WithSystemRuntimeCacheHandle().EnableStatistics();
settings.WithUpdateMode(CacheUpdateMode.None);
});
// all cache layer hold the ITCompanies.
manager.Add(Company.CACHE_KEY, Company.ITCompanies);
manager.Put(Company.CACHE_KEY, Company.MovieCompanies);
manager.Remove(Company.CACHE_KEY);
manager.Add(Company.CACHE_KEY, Company.ITCompanies);
var cachesFinal = manager.Get(Company.CACHE_KEY);
foreach (var handle in manager.CacheHandles)
{
var stats = handle.Stats;
Console.WriteLine(string.Format(
"Items: {0}, Hits: {1}, Miss: {2}, Remove: {3}, ClearRegion: {4}, Clear: {5}, Adds: {6}, Puts: {7}, Gets: {8}",
stats.GetStatistic(CacheStatsCounterType.Items),
stats.GetStatistic(CacheStatsCounterType.Hits),
stats.GetStatistic(CacheStatsCounterType.Misses),
stats.GetStatistic(CacheStatsCounterType.RemoveCalls),
stats.GetStatistic(CacheStatsCounterType.ClearRegionCalls),
stats.GetStatistic(CacheStatsCounterType.ClearCalls),
stats.GetStatistic(CacheStatsCounterType.AddCalls),
stats.GetStatistic(CacheStatsCounterType.PutCalls),
stats.GetStatistic(CacheStatsCounterType.GetCalls)
));
}
支持Cache在各种Operation下的日志记录。
var manager = CacheFactory.Build>(settings =>
{
settings.WithDictionaryHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromSeconds(1)).EnableStatistics().EnablePerformanceCounters();
settings.WithSystemRuntimeCacheHandle().EnableStatistics();
settings.WithUpdateMode(CacheUpdateMode.None);
settings.WithLogging(typeof(CustomerLogFactory));
});
// all cache layer hold the ITCompanies.
manager.Add(Company.CACHE_KEY, Company.ITCompanies);
manager.Put(Company.CACHE_KEY, Company.MovieCompanies);
manager.Remove(Company.CACHE_KEY);
manager.Add(Company.CACHE_KEY, Company.ITCompanies);
var caches = manager.Get(Company.CACHE_KEY);
Assert.That(caches, Is.Not.Null);
OutPut Like Below:
CacheManager.Core.BaseCacheManager