Pet*_*ala 4 c# memorycache asp.net-core asp.net-core-3.1
我们即将使用 ASP.NET Core 的内置内存缓存解决方案来缓存外部系统响应。(我们可能会从内存中转移到IDistributedCache以后。)
我们想使用Mircosoft.Extensions.Caching.Memory的,IMemoryCache正如MSDN 建议的那样。
我们需要限制缓存的大小,因为默认情况下它是无界的。
因此,在将它集成到我们的项目中之前,我创建了以下 POC 应用程序来使用它。
public interface IThrottledCache
{
IMemoryCache Cache { get; }
}
public class ThrottledCache: IThrottledCache
{
private readonly MemoryCache cache;
public ThrottledCache()
{
cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 2
});
}
public IMemoryCache Cache => cache;
}
Run Code Online (Sandbox Code Playgroud)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IThrottledCache>(new ThrottledCache());
}
Run Code Online (Sandbox Code Playgroud)
我已经创建了一个非常简单的控制器来使用这个缓存。
[Route("api/[controller]")]
[ApiController]
public class MemoryController : ControllerBase
{
private readonly IMemoryCache cache;
public MemoryController(IThrottledCache cacheSource)
{
this.cache = cacheSource.Cache;
}
[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (cache.TryGetValue(id, out var cachedEntry))
{
return Ok(cachedEntry);
}
else
{
var options = new MemoryCacheEntryOptions { Size = 1, SlidingExpiration = TimeSpan.FromMinutes(1) };
cache.Set(id, $"{id} - cached", options);
return Ok(id);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我的/api/memory/{id}端点可以在两种模式下工作:
我观察到以下奇怪的行为:
/api/memory/firstfirstfirst/api/memory/firstfirst - cachedfirst/api/memory/secondsecondfirst,second/api/memory/secondsecond - cachedfirst,second/api/memory/thirdthirdfirst,second/api/memory/thirdthirdsecond,third/api/memory/thirdthird - cachedsecond,third正如您在第 5 个端点调用中看到的那样,我达到了限制。所以我的期望如下:
first最旧的条目third为最新的但是这种期望的行为只发生在第 6 次调用时。
所以,我的问题是,Set当达到大小限制时,为什么我必须调用两次才能将新数据放入 MemoryCache?
编辑:也添加时序相关信息
在测试过程中,整个请求流/链花费了大约 15 秒或更短的时间。
即使我将其更改SlidingExpiration为 1 小时,行为仍然完全相同。
我在Microsoft.Extensions.Caching.Memory 中下载、构建和调试单元测试;似乎没有真正涵盖这种情况的测试。
原因是:一旦您尝试添加一个会使缓存超出容量的项目,MemoryCache 就会在后台触发压缩。这将驱逐最旧的 (MRU) 缓存条目,直到出现一定差异。在这种情况下,它会尝试删除总大小为 1 的缓存项,在您的情况下是“第一个”,因为它是最后访问的。
但是,由于这个紧凑循环在后台运行,并且SetEntry()方法中的代码已经在完整缓存的代码路径上,因此它会继续而不将项目添加到缓存中。
下次它尝试时,它成功了。
复述:
class Program
{
private static MemoryCache _cache;
private static MemoryCacheEntryOptions _options;
static void Main(string[] args)
{
_cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 2
});
_options = new MemoryCacheEntryOptions
{
Size = 1
};
_options.PostEvictionCallbacks.Add(new PostEvictionCallbackRegistration
{
EvictionCallback = (key, value, reason, state) =>
{
if (reason == EvictionReason.Capacity)
{
Console.WriteLine($"Evicting '{key}' for capacity");
}
}
});
Console.WriteLine(TestCache("first"));
Console.WriteLine(TestCache("second"));
Console.WriteLine(TestCache("third")); // starts compaction
Thread.Sleep(1000);
Console.WriteLine(TestCache("third"));
Console.WriteLine(TestCache("third")); // now from cache
}
private static object TestCache(string id)
{
if (_cache.TryGetValue(id, out var cachedEntry))
{
return cachedEntry;
}
_cache.Set(id, $"{id} - cached", _options);
return id;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
515 次 |
| 最近记录: |