理解和实现 ASP.NET 核心缓存

[删除(380066935@qq.com或微信通知)]

更好的阅读体验请查看原文:https://www.mitchelsellers.com/blog/article/understanding-implementing-caching-in-asp-net-core

在之前的一篇文章中,我讨论了可扩展性和冗余的架构。提高应用程序性能的一个关键方法是实施缓存来存储关键信息,以防止额外的服务器处理。.NET 提供两种主要缓存机制,用于单个对象的编程缓存;在内存缓存和分布式缓存中。除了这些用于服务器端代码的传统缓存方法之外,还存在一个响应缓存中间件,允许缓存整个 HTTP 响应,从而进一步提高应用程序性能。对于那些没有在其他项目中这样做的人来说,理解和实现这些概念通常是困难的。

何时开始

通常,我会遇到在构建应用程序后只考虑缓存机制的开发团队。为这种做法辩护的最常见声明之一是“在我们看到数据之前,我不知道要缓存什么。尽管可以根据真实数据查看问题,但您最终会追逐红鲱鱼而不是改进您的应用程序。

如果在开始一个项目时清楚地了解未来状态和可用的工具/功能,则可以利用这些并构建一个具有无缝增长潜力和最少初始工作的框架。

了解您的应用程序

通过花时间了解基础知识,可以使用很好的缓存方法和提高性能来启动应用程序,而不是在事后添加它。尽管通过引用的链接进行了完整记录,但应该更多地注意和理解这些概念的实际应用。当我们查看缓存实现时,我们必须首先查看我们的应用程序,以帮助提供通往正确体系结构的途径。

用于缓存的元素

最好的开始方法是查看您的应用程序,查找检索成本高昂经常使用的信息。这些是最常见的操作类型,可以从实施缓存策略中受益,并将减少系统上的整体负载,从而实现更高的吞吐量等。现在,重要的是要查看这些操作的总成本,因为要缓存的信息类型可能会因成本最高的位置/时间/内容而异。它可能缓存原始数据以限制数据库负载,也可能意味着缓存完整显示结果以限制数据库和 Web 服务器资源的使用。

此类别中常见的一些内容:

  • 常用动态菜单
  • 用户特定权限缓存

通过识别其中每个项,了解哪个触发器可能会更改数据以确保您具有适当的控件来清除或以其他方式在完成后更新缓存非常重要。

了解增长机会

此外,在查看您的应用程序时,审查您的未来计划和规模考虑因素至关重要。例如,如果将来以异地冗余实现为目标,则很容易做出实现决策;始终使用分布式缓存。如果你的目标不那么崇高,你可以实现不同风格的缓存;但是,请了解这样做会带来哪些限制。

IMemoryCache 的使用

内存缓存通常是开发人员找到的第一个解决方案,易于使用并且具有简单的 API。只需注入一个 IMemoryCache 对象,并使用提供的方法根据您的特定对象类型获取/设置值。下面的示例演示如何从缓存或本地位置获取值。

IMemoryCache 用法示例
if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
{
    cacheValue = DateTime.Now; //Wasn't found set it in cache
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        .SetSlidingExpiration(TimeSpan.FromSeconds(3));
    _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
}
return cacheValue
    

如您所见,此缓存实现简单、强类型且易于使用。用法的缺点只是因为它是基于内存的,并且在扩展到多台计算机的情况下不起作用。

IDistributedCache 的用法

IDistributedCache也使用了类似的进程;但是,不存在允许类型化检索的帮助程序方法,并且值的设置更加复杂。以下代码用于从缓存中检索值。

IDistributedCache 用法示例 - 获取值
byte[] encodedCacheValue = await _cache.GetAsync("MyKey");
if(encodedCacheValue != null)
{
    return Encoding.UTF8.GetString(encodedCacheValue);
}
else
{
    return "Not Found"
}
    

要更新/设置缓存中的值,您必须做更多的工作。

IDistributedCache 使用示例 - 设置/更新
var myValueToStore = DateTime.UtcNow.ToString();
byte[] encodedValue = Encoding.UTF8.GetBytes(myValueToStore);
var options = new DistributedCacheEntryOptions()
    .SetSlidingExpiration(TimeSpan.FromSeconds(30));
await _cache.SetAsync("MyKey", encodedValue, options);
    

此 API 差异是因为所有缓存的数据都必须分发到外部系统,因此格式需要易于序列化/可传输。此缓存的备份存储可能是内存、数据库、Redis 或其他存储。正是实现中的这种额外复杂性通常会促使开发人员回退到 IMemoryCaching,但这也是为什么以后进行此更改比从一开始就以这种方式实现它的成本更高的主要原因。

但我不想为Redis/SQL付费

这句话是我在项目中使用 IDistributedCache 的常见反击。但是,请务必注意,如果未提供任何其他配置,IDistributedCache 将默认使用内存中缓存,不需要额外的资源。未来的升级很容易。

选择您的路径

有了这些信息,您必须为您的应用程序做出自己的选择。我发现,虽然工作量更大,但从 IDistributedCache 开始可能会导致以后需要快速缩放或其他更改时的工作要少得多。在下面分享您的经验!