繁体   English   中英

Net Core MemoryCache 在 Azure 中无法正常工作

[英]Net Core MemoryCache not working properly in Azure

我注意到一个与在 Net Core 2.2 开发的 API 中使用 MemoryCache 相关的奇怪行为

经过几次测试后,我创建了这个简单的控制器来演示该行为。

API 已发布到 Azure。 在我当地的环境中一切正常。

控制器有三种方法:

  • 创建:在内存缓存中写入四项
  • List:列出内存缓存中存在的元素
  • 清除:从内存缓存中删除项目
    [Route("api/[controller]")]
    public class CacheController : ControllerBase
    {
        private readonly IMemoryCache memoryCache;

        public CacheController(IMemoryCache memoryCache)
        {
            this.memoryCache = memoryCache;
        }

        [HttpGet("Create")]
        public IActionResult CreateEntries()
        {

            memoryCache.Set("Key1", "Value1", TimeSpan.FromHours(1));
            memoryCache.Set("Key2", "Value2", TimeSpan.FromHours(1));
            memoryCache.Set("Key3", "Value3", TimeSpan.FromHours(1));
            memoryCache.Set("Key4", "Value4", TimeSpan.FromHours(1));

            return Ok();
        }


        [HttpGet()]
        public IActionResult List()
        {
            // Get the empty definition for the EntriesCollection
            var cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Public);

            // Populate the definition with your IMemoryCache instance.  
            // It needs to be cast as a dynamic, otherwise you can't
            // loop through it due to it being a collection of objects.
            var cacheEntriesCollection = cacheEntriesCollectionDefinition.GetValue(memoryCache) as dynamic;

            // Define a new list we'll be adding the cache entries too
            var cacheCollectionValues = new List<KeyValuePair<string, string>>();

            foreach (var cacheItem in cacheEntriesCollection)
            {
                // Get the "Value" from the key/value pair which contains the cache entry   
                Microsoft.Extensions.Caching.Memory.ICacheEntry cacheItemValue = cacheItem.GetType().GetProperty("Value").GetValue(cacheItem, null);

                if (!cacheItemValue.Key.ToString().StartsWith("Microsoft.EntityFrameworkCore"))
                    // Add the cache entry to the list
                    cacheCollectionValues.Add(new KeyValuePair<string, string>(cacheItemValue.Key.ToString(), cacheItemValue.Value.ToString()));
            }
            return Ok(cacheCollectionValues);
        }

        [HttpGet("Clear")]
        public IActionResult Clear()
        {
            var removedKeys = new List<string>();
            // Get the empty definition for the EntriesCollection
            var cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Public);

            // Populate the definition with your IMemoryCache instance.  
            // It needs to be cast as a dynamic, otherwise you can't
            // loop through it due to it being a collection of objects.
            var cacheEntriesCollection = cacheEntriesCollectionDefinition.GetValue(memoryCache) as dynamic;

            // Define a new list we'll be adding the cache entries too
            var cacheCollectionValues = new List<KeyValuePair<string, string>>();

            foreach (var cacheItem in cacheEntriesCollection)
            {
                // Get the "Value" from the key/value pair which contains the cache entry   
                Microsoft.Extensions.Caching.Memory.ICacheEntry cacheItemValue = cacheItem.GetType().GetProperty("Value").GetValue(cacheItem, null);

                if (!cacheItemValue.Key.ToString().StartsWith("Microsoft.EntityFrameworkCore"))
                {
                    // Remove the cache entry from the list
                    memoryCache.Remove(cacheItemValue.Key.ToString());
                    removedKeys.Add(cacheItemValue.Key.ToString());
                }
            }
            return Ok(removedKeys);
        }
    }

1) 执行列表动作。 预期结果:无元素 - 获得的结果:无元素 - 正常

2) 执行创建动作。

3) 执行列表动作。 预期结果:4 个元素 - 获得的结果:4 个元素

4) 再次执行列表动作。 预期结果:4 个元素 - 获得的结果:0 个项目 - 错误!!!

5) 执行清除动作。 预期结果:已删除 4 个项目的列表 - 获得的结果:已删除 4 个项目的列表 - 确定

6) 执行列表动作。 预期结果:0 个项目 - 获得的结果:0 个元素 - 确定

7) 执行列表动作。 预期结果:0 个项目 - 获得的结果:4 个元素 - 错误!!!

谁能向我解释这种奇怪的行为?

您没有提供有关实际托管设置的任何信息,但听起来您有多个实例正在运行,即 IIS 中的网络场、容器副本等。

内存缓存是进程绑定的,应用程序的每个运行实例都是不同的进程(每个进程都有自己的内存池)。 因此,关于请求命中哪个进程,因此,当该请求执行时,内存中实际存在哪些数据成为抽签的运气。

如果您需要从请求到请求的缓存一致性,您应该改用IDistributedCache ,以及像 Redis 或 SQL Server 这样的后备存储。 有一个MemoryDistributedCache实现,但这只是开发的一个明智的默认设置; 它实际上并不是分布式的,并且会遇到与MemoryCache相同的进程绑定问题。

否则,您需要确保只有一个应用程序实例在运行,这甚至可能无法实现,具体取决于您的部署方式并且没有扩展潜力,或者您必须接受缓存有时会存在或不取决于在那个过程中发生了什么。 有时候这没关系。 例如,如果你只是缓存一些 API 调用的结果,你可以使用GetOrCreateAsync ,最坏的情况是有时 API 会被再次查询,但它仍然总体上减少了 API 的负载/让你处于任何速率限制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM