热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

asp.netcore系列之Reponsecaching之cacheinmemory(2)

这篇文章(主要翻译于官网,水平有限,见谅)讲解asp.netcore中的Cachein-memory(内存缓存).  CachingbasicsCaching可以显著的提升应用的p

这篇文章(主要翻译于官网,水平有限,见谅)讲解asp.net core 中的 Cache in-memory (内存缓存).

 

 


Caching basics

Caching 可以显著的提升应用的performance(表现) scalability,通过减少生成内容所必需的做的工作Caching 在变动比较的数据上工作的***。Caching 可以做一个备份数据,使得数据比从原来的地方取的快一些。

ASP.NET Core支持几种不同的缓存。最简单的缓存是基于IMemoryCache, 它代表一个存储在web服务器的内存上的cache(缓存)当使用in-memory cache时,运行在多个服务器上的服务器集群应该确保sessions是不动的不动的sessionsSticky sessions)确保随后的从一个client发来的请求全都到同一台服务器。例如,Azure Web appsApplication Request Routing(ARR)来路由所有随后的请求到同一个服务器。

在一个web集群上的Non-sticky sessions 要求一个distributed cache(分布式缓存)来避免缓存一致性问题。对于一些应用,a distributed cache 可以支持更高的扩展比in-memory cache. 用一个分布式缓存卸载内存缓存到一个外部处理中。

In-memory cache 可以存储任意对象;distributed cache interface 仅限于byte[]. 对于in-memorydistributed cache 存储cache itemskey-value pairs. 


System.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache可以被用在:



  • .NET Standard 2.0 or later



  • Any .NET implementation that targets .NET Standard 2.0 or later. 例如, ASP.NET Core 2.0 or later



  • .NET Framework 4.5 or later



Microsoft.Extensions.Caching.Memory/IMemoryCache 被推荐在System.Runtime.Cachign/MemoryCache之上使用, 因为Microsoft.Extensions.Caching.Memory/IMemoryCache是更好的集成在ASP.NET Core中。例如,IMemory 天生可以用ASP.NET Core的依赖注入工作。

System.Runtime.Caching/MemoryCache作为一个兼容桥梁,当移植代码从ASP.NET 4.X ASP.NET Core时。


Cache guidelines



  • 代码应该总有一个可靠的选项来取数据并且不是依赖于缓存的可得到的值



  •  缓存使用稀少的资源,内存。限制缓存增长(cache growth)(内存是稀缺资源, 如果在内存中使用缓存,需要限制缓存增长)



    • 不要使用外部输入作为cache keys.



    • 使用expirations(过期时间)限制缓存增长



    • 使用SetSize, SizeSizeLimit来限制cache size.  ASP.NET Core runtime不会根据memory pressure(内存压力)来限制cache size,它取决于开发者限制cache size. 






Using IMemoryCache

In-memory caching 是一个从你的应用中使用依赖注入引入的服务(service)。在ConfigureServices中调用 AddMemoryCache:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}

Request the IMemoryCache实例在构造函数中:

public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache
= memoryCache;
}

IMemoryCache要求有NuGet package Microsoft.Extensions.Caching.Memory, 它在Microsoft.AspNetCore.App metapackage也是可用的

下面的代码使用 TryGetValue 来检验 if a time is in the cache. If a time isn’t cached, a new entry is created and added to the cache with Set. 检验一个时间值是否在缓存中。如果时间值没有被缓存,一个新的entry被创建并且with Set加入到缓存中。(即,如果没被缓存,则加入缓存)

public static class CacheKeys
{
public static string Entry { get { return "_Entry"; } }
public static string CallbackEntry { get { return "_Callback"; } }
public static string CallbackMessage { get { return "_CallbackMessage"; } }
public static string Parent { get { return "_Parent"; } }
public static string Child { get { return "_Child"; } }
public static string DependentMessage { get { return "_DependentMessage"; } }
public static string DependentCTS { get { return "_DependentCTS"; } }
public static string Ticks { get { return "_Ticks"; } }
public static string CancelMsg { get { return "_CancelMsg"; } }
public static string CancelTokenSource { get { return "_CancelTokenSource"; } }
}

 

public IActionResult CacheTryGetValueSet()
{
DateTime cacheEntry;
// Look for cache key.
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now;
// Set cache options.
var cacheEntryOptiOns= new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
// Save data in cache.
_cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
}
return View("Cache", cacheEntry);
}

当前时间和缓存时间都被展示了:

@model DateTime?


Actions



  • "Home" asp-action="CacheTryGetValueSet">TryGetValue and Set

  • "Home" asp-action="CacheGet">Get

  • "Home" asp-action="CacheGetOrCreate">GetOrCreate

  • "Home" asp-action="CacheGetOrCreateAsync">GetOrCreateAsync

  • "Home" asp-action="CacheRemove">Remove



Current Time: @DateTime.Now.TimeOfDay.ToString()


Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())

requests在超时时间之内时,缓存的时间值保留在缓存中。下面的图片展示了当前时间和从缓存中检索的更早的时间。

下面的代码使用GetOrCreate和GetOrCreateAsync来缓存数据。

public IActionResult CacheGetOrCreate()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration
= TimeSpan.FromSeconds(3);
return DateTime.Now;
});
return View("Cache", cacheEntry);
}
public async Task CacheGetOrCreateAsync()
{
var cacheEntry = await
_cache.GetOrCreateAsync(CacheKeys.Entry, entry
=>
{
entry.SlidingExpiration
= TimeSpan.FromSeconds(3);
return
Task.FromResult(DateTime.Now);
})
;
return View("Cache", cacheEntry);
}

下面的代码调用Get来取到缓存时间:

public IActionResult CacheGet()
{
var cacheEntry = _cache.Get(CacheKeys.Entry);
return View("Cache", cacheEntry);
}

GetOrCreate, GetOrCreateAsyc, 和Get 是CacheExtensions类的扩展方法的一部分,CacheExtension类扩展了IMemory的能力。 


MemoryCacheEntryOptions

下面的例子(用来设置内存缓存的一些选项):




  • 设置完全的expiration time(超时时间)。这是这个记录可以被缓存的最大时间,并且可以防止这个记录变的太陈旧,当变化的expiration 是不断更新的。


  • 设置一个变化的expiration time. 到达这个缓存(cache item)的请求将重新设置变化的时间。

  • 给CacheItemPriority.NeverRemove设置cache priority(缓存优先级)


  • 设置PostEvictionDelegate(回调),它将会被调用,在记录被从缓存驱逐之后Callback(回调)是运行在一个不同于从缓存中移除缓存项的线程的其他线程。(Callback运行在一个区别于移除缓存项线程的其他线程)

public IActionResult CreateCallbackEntry()
{
var cacheEntryOptiOns= new MemoryCacheEntryOptions()
// Pin to cache.
.SetPriority(CacheItemPriority.NeverRemove)
// Add eviction callback
.RegisterPostEvictionCallback(callback: EvictionCallback, state: this);
_cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);
return RedirectToAction("GetCallbackEntry");
}
public IActionResult GetCallbackEntry()
{
return View("Callback", new CallbackViewModel
{
CachedTime
= _cache.Get(CacheKeys.CallbackEntry),
Message
= _cache.Get<string>(CacheKeys.CallbackMessage)
});
}
public IActionResult RemoveCallbackEntry()
{
_cache.Remove(CacheKeys.CallbackEntry);
return RedirectToAction("GetCallbackEntry");
}
private static void EvictionCallback(object key, object value,
EvictionReason reason,
object state)
{
var message = $"Entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

使用SetSize, SizeSizeLImit来限制 cache size

一个MemoryCache实例可以选择指定或者强制一个size limitThe memory size limit 没有一个定义的测量单元,因为cache没有结构来测量记录(entries)大小(size). 如果cache memory size limit被设置了,所有的entries必须指定size.  ASP.NET Core runtime不会根据memory pressure来limit cache size . 它取决于开发者limit cache size. The size spcified is in units the developer chooses. 

MemoryCache instance may optionally specify and enforce a size limit. The memory size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. If the cache memory size limit is set, all entries must specify size. The ASP.NET Core runtime does not limit cache size based on memory pressure. It's up to the developer to limit cache size. The size specified is in units the developer chooses.

例如:



  • 如果一个web应用主要caching string , 每个cache entry size应该是字符串长度



  • 应用可以指定the size of all entries (所有的entry)1,并且这个size limitthe count of entries. (注意:这里指定所有的entry的大小为1,则size limit可以用entry的数量表示。即两者一个是cache entry size(单个entry大小),另一个是limit size(缓存限制的大小))



下面的代码创建了一个unitless fiexed size MemoryCache accessible(易接近的) by dependency injection:

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache
{
public MemoryCache Cache { get; set; }
public MyMemoryCache()
{
Cache
= new MemoryCache(new MemoryCacheOptions
{
SizeLimit
= 1024
});
}
}

SizeLimit没有 units .   如果cache memory size 已经被设置,Cached entries 必须指定最合适的size in whatever units they deem(认为)。一个cache 实例的所有用户应该用同样的unit system . 如果the sum of the cached entry sizes 超过通过SizeLimit指定的值, An entry 将不会被缓存如果no cache size limit被设置,the cache size set on the entry 将会被忽略。

SizeLimit does not have units. Cached entries must specify size in whatever units they deem most appropriate if the cache memory size has been set. All users of a cache instance should use the same unit system. An entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. If no cache size limit is set, the cache size set on the entry will be ignored.

下面的代码使用依赖注入容器注册MyMemoryCache. 

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton
();
}

MyMemoryCache被创建为一个independent memory cache 的组件,这个组件了解size limited cahce并且知道怎么合适的设置cache entry size 。

下面是使用MyMemoryCache的代码:

public class AboutModel : PageModel
{
private MemoryCache _cache;
public static readonly string MyKey = "_MyKey";
public AboutModel(MyMemoryCache memoryCache)
{
_cache
= memoryCache.Cache;
}
[TempData]
public string DateTime_Now { get; set; }
public IActionResult OnGet()
{
if (!_cache.TryGetValue(MyKey, out string cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now.TimeOfDay.ToString();
var cacheEntryOptiOns= new MemoryCacheEntryOptions()
// Set cache entry size by extension method.
.SetSize(1)
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
// Set cache entry size via property.
// cacheEntryOptions.Size = 1;
// Save data in cache.
_cache.Set(MyKey, cacheEntry, cacheEntryOptions);
}
DateTime_Now
= cacheEntry;
return RedirectToPage("./Index");
}
}

The size of the cache entry 可以被设置,通过SizeSetSize扩展方法

public IActionResult OnGet()
{
if (!_cache.TryGetValue(MyKey, out string cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now.TimeOfDay.ToString();
var cacheEntryOptiOns= new MemoryCacheEntryOptions()
// Set cache entry size by extension method.
.SetSize(1
)
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
// Set cache entry size via property.
//
cacheEntryOptions.Size = 1;
// Save data in cache.
_cache.Set(MyKey, cacheEntry, cacheEntryOptions);
}
DateTime_Now
= cacheEntry;
return RedirectToPage("./Index");
}

Cache dependencies

下面的示例展示了怎么设置过期一个缓存记录(how to expire a cache entry)如果一个dependent entry expires(过期). 一个CancellationChangeToken 被加入到cached item. Cancel CancellationTokenSource上被调用,两个cache entry 都被抛弃

public IActionResult CreateDependentEntries()
{
var cts = new CancellationTokenSource();
_cache.Set(CacheKeys.DependentCTS, cts);
using (var entry = _cache.CreateEntry(CacheKeys.Parent))
{
// expire this entry if the dependant entry expires.
entry.Value = DateTime.Now;
entry.RegisterPostEvictionCallback(DependentEvictionCallback,
this);
_cache.Set(CacheKeys.Child,
DateTime.Now,
new CancellationChangeToken(cts.Token));
}
return RedirectToAction("GetDependentEntries");
}
public IActionResult GetDependentEntries()
{
return View("Dependent", new DependentViewModel
{
ParentCachedTime
= _cache.Get(CacheKeys.Parent),
ChildCachedTime
= _cache.Get(CacheKeys.Child),
Message
= _cache.Get<string>(CacheKeys.DependentMessage)
});
}
public IActionResult RemoveChildEntry()
{
_cache.Get
(CacheKeys.DependentCTS).Cancel();
return RedirectToAction("GetDependentEntries");
}
private static void DependentEvictionCallback(object key, object value,
EvictionReason reason,
object state)
{
var message = $"Parent entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}

使用CancellationTokenSource允许多个cache entries作为一组被抛弃。使用代码中有用的模式,cache entires

Created inside the using block will inherit triggers and expiration settings. 


Additional notes




  • 当用一个回调来增加一个cache item:



    • 可以发现多个requests缓存的键的value 空.因为回调没有完成。



    • 这可能会导致几个线程增加cached item






  • 当一个cache entry被使用来创建另一个cache entry,child复制父母亲的entryexpration tokens 并且基于时间的exiration settings. THe child不会通过手动删除或者parent entry的更新而过期



  • PostEvictionCallback来设置callback, 这个callback将会被触发。



参考资料:

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-2.2

 



推荐阅读
  • 使用 ModelAttribute 实现页面数据自动填充
    本文介绍了如何利用 Spring MVC 中的 ModelAttribute 注解,在页面跳转后自动填充表单数据。主要探讨了两种实现方法及其背后的原理。 ... [详细]
  • selenium通过JS语法操作页面元素
    做过web测试的小伙伴们都知道,web元素现在很多是JS写的,那么既然是JS写的,可以通过JS语言去操作页面,来帮助我们操作一些selenium不能覆盖的功能。问题来了我们能否通过 ... [详细]
  • 本文探讨了在AspNetForums平台中实施基于角色的权限控制系统的方法,旨在为不同级别的用户提供合适的访问权限,确保系统的安全性和可用性。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 本文详细介绍了如何利用 Bootstrap Table 实现数据展示与操作,包括数据加载、表格配置及前后端交互等关键步骤。 ... [详细]
  • PHP面试题精选及答案解析
    本文精选了新浪PHP笔试题及最新的PHP面试题,并提供了详细的答案解析,帮助求职者更好地准备PHP相关的面试。 ... [详细]
  • 电商高并发解决方案详解
    本文以京东为例,详细探讨了电商中常见的高并发解决方案,包括多级缓存和Nginx限流技术,旨在帮助读者更好地理解和应用这些技术。 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • Python网络编程:深入探讨TCP粘包问题及解决方案
    本文详细探讨了TCP协议下的粘包现象及其产生的原因,并提供了通过自定义报头解决粘包问题的具体实现方案。同时,对比了TCP与UDP协议在数据传输上的不同特性。 ... [详细]
  • Hibernate全自动全映射ORM框架,旨在消除sql,是一个持久层的ORM框架1)、基础概念DAO(DataAccessorOb ... [详细]
  • 本文详细介绍了在Windows系统中如何配置Nginx以实现高效的缓存加速功能,包括关键的配置文件设置和示例代码。 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
author-avatar
EksManhn_188
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有