ASP.NET Core - 缓存之内存缓存(下)

科技资讯 投稿 8400 0 评论

ASP.NET Core - 缓存之内存缓存(下)

2.4 MemoryCacheEntryOptions

MemoryCacheEntryOptions 是内存缓存配置类,可以通过它配置缓存相关的策略。除了上面讲到的过期时间,我们还能够设置下面这些:

    设置缓存优先级。
  • 设置在从缓存中逐出条目后调用的 PostEvictionDelegate。
    回调在与从缓存中删除项的代码不同的线程上运行。
  • 限制缓存大小
var memoryCacheEntryOption = new MemoryCacheEntryOptions(;
memoryCacheEntryOption.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3;
// 缓存优先级,Low, Normal, High, NeverRemove,Normal是默认值,与缓存删除时的策略有关
memoryCacheEntryOption.SetPriority(CacheItemPriority.Normal;
// 注册缓存项删除回调事件
memoryCacheEntryOption.RegisterPostEvictionCallback(PostEvictionDelegate;
_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption;

public void PostEvictionDelegate(object cacheKey, object cacheValue, EvictionReason evictionReason, object state
{
	var memoryCache = (IMemoryCachestate;

	Console.WriteLine($"Entry {cacheKey}:{cacheValue} was evicted: {evictionReason}.";
}

例如:

    如果 Web 应用主要缓存字符串,则每个缓存条目的大小可以是字符串长度。
  • 应用可以将所有条目的大小指定为 1,大小限制是条目计数。
    如果未设置 SizeLimit,则缓存会无限增长。 系统内存不足时,ASP.NET Core 运行时不会剪裁缓存。 应用必须构建为:
    • 限制缓存增长。
    • 在可用内存受限时调用 Compact 或 Remove。
// 我们可以在进行内存缓存注册的时候设置缓存大小限制
services.AddMemoryCache(options =>
{
	options.SizeLimit = 1024;
};

// 之后设定每个缓存项的大小,可根据开发人员的判断设置,例如这里无论多大都设置为 1,则最多有 1024 个缓存项
memoryCacheEntryOption.SetSize(1;
_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption;

2.5 缓存清理

缓存到期后不会在后台自动清理。 没有计时器可以主动扫描缓存中的到期项。 而缓存上的任何活动(Get、Set、Remove)都可触发在后台扫描到期项。 如果使用了CancellationTokenSource,则其计时器 (CancelAfter 也会删除条目并触发扫描到期项,这个是下面要讲到的。

_cache.Remove(cacheKey;
_cache.Compact(.25;

Compact 方法会尝试按以下顺序删除指定百分比的缓存:

    所有到期项。
  • 按优先级排列的项。 首先删除最低优先级的项。
  • 最近最少使用的对象。
  • 绝对到期时间最短的项。
  • 可调到期时间最短的项。

2.6 缓存组

缓存项的过期策略除了上面讲到的过期时间的设置之外,还可以通过 CancellationChangeToken 来控制,通过它可以同时控制多个缓存对象的过期策略,实现相关的缓存同时过期,构成一个组的概念。

首先先定义两个方法,将缓存设置和缓存读取分开:

public interface ICacheService
{
	public void PrintDateTimeNow(;

	public void SetGroupDateTime(;

	public void PrintGroupDateTime(;
}

public class CacheService : ICacheService 
{
	public const string CacheKey = "CacheTime";
	public const string DependentCancellationTokenSourceCacheKey = "DependentCancellationTokenSource";
	public const string ParentCacheKey = "Parent";
	public const string ChildCacheKey = "Chlid";
	private readonly IMemoryCache _cache;
	public CacheService(IMemoryCache memoryCache
	{
		_cache = memoryCache;
	}

	public void PrintDateTimeNow(
	{
		var time = DateTime.Now;
		if (!_cache.TryGetValue(CacheKey, out DateTime cacheValue
		{
			cacheValue = time;
			// 设置绝对过期时间
			// 两种实现的功能是一样的,只是时间设置的方式不同而已
			// 传入的是 AbsoluteExpirationRelativeToNow, 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
			// _cache.Set(CacheKey, cacheValue, TimeSpan.FromSeconds(2;
			// 传入的是 AbsoluteExpiration,绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
			// _cache.Set(CacheKey, cacheValue, DateTimeOffset.Now.AddSeconds(2;

			//var memoryCacheEntryOption = new MemoryCacheEntryOptions(;
			//// 滑动过期时间是一个相对时间
			//memoryCacheEntryOption.SlidingExpiration = TimeSpan.FromSeconds(3;
			//_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption;

			var memoryCacheEntryOption = new MemoryCacheEntryOptions(;
			memoryCacheEntryOption.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3;
			// 缓存优先级,Low, Normal, High, NeverRemove,Normal是默认值,与缓存删除时的策略有关
			memoryCacheEntryOption.SetPriority(CacheItemPriority.Normal;
			memoryCacheEntryOption.RegisterPostEvictionCallback(PostEvictionDelegate;
			// 之后设定每个缓存项的大小,可根据开发人员的判断设置,例如这里无论多大都设置为 1,则最多有 1024 个缓存项
			memoryCacheEntryOption.SetSize(1;
			_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption;
		}
		time = cacheValue;

		Console.WriteLine("缓存时间:" + time.ToString("yyyy-MM-dd HH:mm:ss";
		Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss";
	}

	public void PostEvictionDelegate(object cacheKey, object cacheValue, EvictionReason evictionReason, object state
	{
		var memoryCache = (IMemoryCachestate;

		Console.WriteLine($"Entry {cacheKey}:{cacheValue} was evicted: {evictionReason}.";
	}

	public void SetGroupDateTime(
	{
		// 这里为了将 CancellationTokenSource 保存起来,以便在外部可以获取得到
		var cancellationTokenSource = new CancellationTokenSource(;
		_cache.Set(
			DependentCancellationTokenSourceCacheKey,
			cancellationTokenSource;

		using var parentCacheEntry = _cache.CreateEntry(ParentCacheKey;

		parentCacheEntry.Value = DateTime.Now;

		Task.Delay(TimeSpan.FromSeconds(1.Wait(;

		_cache.Set(
			ChildCacheKey,
			DateTime.Now,
			new CancellationChangeToken(cancellationTokenSource.Token;
	}

	public void PrintGroupDateTime(
	{
	   if(_cache.TryGetValue(ParentCacheKey, out DateTime parentCacheDateTime
		{
			Console.WriteLine("ParentDateTime:" + parentCacheDateTime.ToString("yyyy-MM-dd HH:mm:ss";
		}
		else
		{
			Console.WriteLine("ParentDateTime is canceled";
		}

		if (_cache.TryGetValue(ChildCacheKey, out DateTime childCacheDateTime
		{
			Console.WriteLine("ChildDateTime:" + childCacheDateTime.ToString("yyyy-MM-dd HH:mm:ss";
		}
		else
		{
			Console.WriteLine("ChildDateTime is canceled";
		}
	}
}

之后改造一下入口文件中的测试代码:

var service = host.Services.GetRequiredService<ICacheService>(;
service.SetGroupDateTime(;

service.PrintGroupDateTime(;

service.PrintGroupDateTime(;

var cache = host.Services.GetRequiredService<IMemoryCache>(;
var cancellationTokenSource = cache.Get<CancellationTokenSource>(CacheService.DependentCancellationTokenSourceCacheKey;
cancellationTokenSource.Cancel(;

service.PrintGroupDateTime(;

从控制台输出可以看得到前两次缓存获取正常,当调用 CancellationTokenSource.Cancel( 方法取消请求之后,缓存过期了。

_cache.Set(ParentCacheKey,
	DateTime.Now,
	new CancellationChangeToken(cancellationTokenSource.Token;

_cache.Set(
	ChildCacheKey,
	DateTime.Now,
	new CancellationChangeToken(cancellationTokenSource.Token;

_cache.Set(
	ChildsCacheKey,
	DateTime.Now,
	new CancellationChangeToken(cancellationTokenSource.Token;

2.7 一些注意事项

    • 多个请求可以发现缓存的键值为空,因为回调没有完成。
    • 这可能会导致多个线程重新填充缓存项。

官方文档上还有另外几条,但是我这边在上面有讲过了,这里就不再重复了。


ASP.NET Core 中的内存中缓存


目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 缓存之内存缓存(上

编程笔记 » ASP.NET Core - 缓存之内存缓存(下)

赞同 (45) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽