在asp.net中如何管理cache

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在asp.net中如何管理cache相关的知识,希望对你有一定的参考价值。

参考技术A

  介绍:

  在我解释cache管理机制时 首先让我阐明下一个观念 IE下面的数据管理 每个人都会用不同的方法去解决如何在IE在管理数据 有的会提到用状态管理 有的提到的cache管理 这里我比较喜欢cache管理 因为本人比较喜 cache 这个词 但是状态管理和cache管理这两个在概念和意义上是不同的 下面就让我们来讨论下两都在各个方面的不同之处

  虽然cache管理并不存在于Windows程序 但在web环境中已经得到巨大的应用 自从HTTP变成无协议以来 在WEB上要想分辨两个不同请求变得非常难 如何分辨如此多的请求变得非常重要 如果是同一请求 我们就可以把数据缓存起来供web上所有用户访问 减少数据重复进行物理加载

  提供了几种方法来缓存数据在客户端和服务器端 但是我们经常为到底用哪种方式而感动苦恼 提供了以下三种实现方式

   :Session; : Application : Cache objects 我们必须非常清楚它们之间的优势 这样才能充分利用它们的优势发挥在web程序中

  背景

  这篇文章中 我将简单涉及cache管理中的不同功能 在web程序中 我们为了避免因高并发产生的数据访问带来的性能问题 我们有必要把数据缓存于服务器端 使得后来的访问可以直接调用缓存数据 起到数据重用的作用

  缓存能够帮忙我们提到服务质量的三个重要方面

  性能 缓存数据达到数据重用 避免了重复的物理数据加载

  可量测性 数据缓存后 减少了从服务器端加载数据

  实用性 如果其它的系统或者是数据库发生死机 那么仍然可以从缓存中取得数据不受局部硬件的影响

  在一个web 程序中 我们可以数据缓存 页面缓存等等 让我们来看下数据缓存在服务器端和客户端的不同之处

   服务器端缓存:

   Session状态管理:

  session为每个人缓存数据 也就是说这样缓存的数据并不能同时供多人共享 限于为单个人缓存数据

  状态管理有三种实现方式 分别是

   :InProc:

  它的数据存储在aspnet_wp exe 进程中 数据会因为IIS的重启而丢失

   :StateServer :

  较InProc不同的是 它可以存储于不同的服务器中

   :SQLServer:

  它的数据存储在数据库中 数据不会因为IIS的重启而丢失数据

  后两种方法与InProc最大的区别在于 我们要确保缓存的数据是可序列化的 否则只能用于第一种方式 为此我们要仔细分析从而选出最适合自己的方式

  下面是如何作用Session的代码片段

  Code

  string empNum = Request QueryString[ empnum ];

  if (empNum != null)

  

  string details = null;

  if (Session[ EMP_DETAILS ] == null)

  

  //Get Employee Details for employee number passed

  string details = GetEmployeeDetails(Convert ToInt (empNum));

  Session[ EMP_DETAILS ] = details;

  

  else

  

  details = Session[ EMP_DETAILS ];

  

  //send it to the browser

  Response Write(details);

  

   ASP NET application

  为我们提供了另外一种全局变量保存方法 Application对象 它也是面向所有用户 它的生命周期和应用程序一样 当应用程序初始化后它就开始重建 但它最大的缺点是没有相关数据过期的方法 此时就要用到cache管理

   ASP NET cache

  cache是我最喜欢用的机制 这也是为什么我喜欢说它的原因 它提供了键 值对应的方法 cache对应的命名空间是 System Web Caching 它的生命周期也依赖于应用程序 但它并不像session 这也是面向所有用户的 虽然cache看起来特别像application 但它最大的不同是提供了数据缓存失效控制方法以及数据缓存依赖管理 也就是说 在cache中我们可以非常容易的按照事先设定好的过期时间来让cache过期 删除cache 我们也可以根据缓存依赖关系来操作cache 当依赖的关系有改动时 此时cache也会自动失效 而这都是applicaion没法办到的

  现在让我们看下中是如何支持cache的过期以及数据缓存依赖的

   :缓存依赖

  顾名思义它是指当事先设定的依赖关系发生变化时 cache 将会失效 在中提供了两种依赖关系

   文件缓存依赖:当磁盘上的一个文件发生变化时自动让cache失效

  下面是实例代码

  object errorData;

  //Load errorData from errors xml

  CacheDependency fileDependency =

  new CacheDependency(Server MapPath( errors xml ));

  Cache Insert( ERROR_INFO errorData fileDependency);

   键值缓存依赖 看起来和文件缓存非常像 不同之外就是这种依赖方式不同而已 当有多个cache信息之间互相关联时 一个cache信息的变化将会引起其它cache的失效 例如一个用户信息包含 编号 姓名 地址等 如果用户编号发生变化则cache失效 这种情况下 用户的基本信息就依赖于用户编号

  下面是示例代码

  string[] relatedKeys = new string[ ];

  relatedKeys[ ] = EMP_NUM ;

  CacheDependency keyDependency = new CacheDependency(null relatedKeys);

  Cache[ EMP_NUM ] = ;

  Cache Insert( EMP_NAME Shubhabrata keyDependency);

  Cache Insert( EMP_ADDR Bhubaneswar keyDependency);

  Cache Insert( EMP_SAL USD keyDependency);

   :过期策略 从创建cache开始 一段时间后自动过期

  示例代码

  //Absolute Expiration

  Cache Insert( EMP_NAME Shubhabrata null

  DateTime Now AddDays( ) Cache NoSlidingExpiration);

  //Sliding Expiration

  Cache Insert( EMP_NAME Shubhabrata null

  Cache NoAbsoluteExpiration TimeSpan FromSeconds( ));

   ASP NET 页面输出缓存

  有的时候在web站点中 有些页面在很长一段时间内都不会发生变化 例如一个招聘网站 它对于工资的描述文字一般不会经常更改 一般都是一个月更改一次 所以在这一个月内 用户看到的内容都是一样的 所有如果把数据缓存在服务器端并不是完美的解决方案 这里可以用页面输出缓存

  下面是示例代码

  <%@OutputCache Duration= VaryByParam= empNum

  Location= Server %>

   :客户端缓存

  在上面的文章中 我讨论了些数据缓存在服务器端的方法 然而有的时候我们为了提高性能我们要把有些数据缓存到客户端 利用这种机制来达到缓解服务器压力 不过在客户端缓存数据会有各种不同的安全性问题 下面我说简要的说下相关内容

   Cookies:cookies在WEB程序开发中应用的非常广泛 它可以非常方便的在客户端与服务器端相互访问 不过它有数据大小限制 最大为 K 所有用它经常是保存小数据 同时cookie对于失效的控制也支持的相当完美

  下面是示例代码

  if (this Request Cookies[ MY_NAME ] == null)

  

  this Response Cookies Add(new HttpCookie( MY_NAME

   Shubhabrata Mohanty ));

  

  else

  

  this Response Write(this Request Cookies[ MY_NAME ] Value);

  

   ViewState:ViewState是一个全新的概念 它一般用于页面或者是控件中保留数据以供和服务端交通 在ASP中的 我们存储数据是用隐藏控件来完成(Hidden fields) ViewState也是这样用的 只不过它比隐藏控件更加安全性 所有的值都是经过hash处理的 如果你查看页面源代码 你都会看到ViewState的存在 一般ViewState不用来保存大的数据

  下面是示例代码

  protected void Page_Load(object sender EventArgs e)

  

  if (this ViewState[ MY_NAME ] == null)

  

  this ViewState[ MY_NAME ] = Shubhabrata Mohanty ;

  

  //txtName is a TextBox control

  this txtName Text = this ViewState[ MY_NAME ] ToString();

  

   隐藏控件 Hidden fields:它是最简单的 不用多说

  下面是示例代码

  <! In ASP NET >

  <asp:HiddenField ID= myHiddenField Value= Shubhabrata

  runat= server />

  <! In html >

lishixinzhi/Article/program/net/201311/12415

Asp.Net Core缓存管理

ASP.NET Core 客户端响应缓存

Cache-control
1.RFC7324是HTTP协议中对缓存进行控制的规范,其中重要的是cache-control这个响应头。服务器如返回cache-control:max-age=60,则表示服务器指示浏览器端"可以缓存这个响应内容60秒"
2.我们只要给需要进行缓存控制的控制器的操作方法添加ResponseCacheAttribut这个Attribute,ASP.NET Core会自动添加cache-control报文头
3.验证:编写一个返回当前的Action方法,分别加和不加ReponseCacheAttribute看区别。也可以F12看看Network

        [ResponseCache(Duration =20)]
        [HttpGet]
        public DateTime Index()
        
            return System.DateTime.Now;
        

ASP.NET Core 服务器端响应缓存(鸡肋)

  1. 如果ASP.NET Core中安装了“响应缓存中间件”,那么ASP.NET Core不仅会继续根据[ReponseCache]设置来生成cache-control响应报文头来设置客户端缓存,而且服务端也会按照[ResponseCache]的设置来对响应进行服务器端缓存。和客户端缓存的区别?来自多个不同客户端的相同请求。
  2. “响应缓存中间件”的好处:对于来自不同客户端的相同请求或者不支持客户端缓存的客户端,能降低服务器端的压力
  3. 用法:app.MapController()之前加上app.UseResponseCaching()。请确保app.UseCors()写到app.UseResponseCaching()之前。

演示效果

  1. 大部分浏览器都是支持RFC7324规范的,所以不方便用来测试服务器端响应缓存。用默认忽略RFC7324规范的PostMan测试。试一下请求服务器端。
  2. 可以浏览器的“开发人员工具”中禁用缓存的,但是和PostMan中不一致,为何?“cache-control:no-cache”
  3. 也可以让Postman在请求报文头中加入"cache-control:no-cache",只要在Postman的设置中开启【Send no-cache headers】

ASP.NET Core 内存缓存

内存缓存(In-memory cache)

  1. 把缓存数据放到运用程序。内存缓存中保存的是一系列的键值怼,就像
    Dictionary类型一样。
  2. 内存缓存的数据保存在当前运行的网站程序的内存中,是和进程相关的。
    因为在WEB服务器中,多个不同的网站时运行在不同的进程中,因此不同
    网站的内存缓存时不会互相干老的,而且网站重启后,村村缓存中的所有数据也都被清空了。

内存缓存用法

  1. 启用:builder.Services.AddMemoryCache()
  2. 注入IMemoryCache接口,查看接口的方法:
TryGetValue,Remove,Set,GetOrCreate,GetOrCreateAsync
3. 用GetOrCreateAsync讲解
Public async Task<Book[]> GetBooks()

Looger.LogInformation("开始执行GetBooks"):
Var items=await memCache.GetOrCreateAsync("AllBooks",async=>
Logger.LogInfromaion("从数据库读取数据");
Return await dbCtx.Books.toArrayAsync();
);
Logger.LogInfromaion("把数据返回给调用者");
 
Return items;

演示:

    public class MyDbContext
    
        public static Task<Book?> GetByIdAsync(long id) 
        
  
            var result = GetById(id);
 
            return Task.FromResult(result);
 
        
        public static Book GetById(long id)
        
            switch (id)
            
                case 0:
                    return new Book(0,"零基础趣学C语言");
                case 1:
                    return new Book(1,"J2EE开发全程实录");
                case 2:
                    return new Book(2, "程序员的SQL金典");
                default:
                    return null;
                    break;
            
        
    
 
    [ApiController]
    [Route("api/[controller]/[action]")]
    public class TestController : Controller
    
        private readonly IMemoryCache memoryCache;
        private readonly ILogger<TestController> logger;
 
 
 
        public TestController(IMemoryCache memoryCache, ILogger<TestController> logger)
        
            this.memoryCache = memoryCache;
            this.logger = logger;
        
 
        [HttpGet]
        public async Task<ActionResult<Book?>> GetBookById(long id) 
        
            Book? result = MyDbContext.GetById(id);
            //GetOrCreateAsync 二合一:1)从缓存取数据 2)从数据源取数据,并且返回给调用者及保存到缓存
            logger.LogDebug($"开始执行GetBookById,id=id");
            Book? b=await memoryCache.GetOrCreateAsync("Book" + id,
                async (e) => 
                    logger.LogDebug($"缓存里没有找到,到数据库中查一查,id=id");
                    return await MyDbContext.GetByIdAsync(id);
            );
            logger.LogDebug($"GetOrCreateAsync结果b");
            if (b == null)
            
                return NotFound($"找不到id=id的书");
            
            else 
            
                return b;   
            
        
 
        [ResponseCache(Duration =20)]
        [HttpGet]
        public DateTime Index()
        
            return System.DateTime.Now;
        
    

ASP.NET Core 缓存的过期时间策略

缓存的过期时间

  1. 上面的例子中的缓存不会过期,除非重启服务器。
  2. 解决办法:在数据改变的时候调用Remove或Set来删除或者修改缓存(优点:及时)过期时间(只要过期时间比较短,缓存数据不一致的清空也不会持续很长时间。)
  3. 两种过期时间策略:绝对过期时间,滑动过期时间。他们分别是什么?
    缓存的绝对过期时间
  4. GetOrCreateAsync()方法的回调方法中有一个ICacheEntry类型的参数,通过ICacheEntry对当前的缓存项做设置。
  5. AbsoluteExpirationRelativeToNow用来设定缓存项的绝对过期时间
    缓存的滑动过期时间
    ICacheEntry的SlidingExpiration属性用来设定缓存项的滑动过期时间
    两种过期时间混用
    使用滑动过期时间策略,如果一个缓存项目一直被频繁访问,那么这个缓存项就会一直被持续而不过期。可以对一个缓存项同时设定滑动过期时间和绝对过期时间,并且把绝对过期时间设定的比滑动过期时间长,这样缓存项的内容会在绝对过期时间内随着访问被滑动续期,但是以一旦超过了绝对过期时间,缓存项目就会被删除。
    内存缓存的是与非
  6. 无论用哪种过期时间策略,程序种都会在缓存数据不一致的情况。部分系统(博客等)无所谓,部分系统不能忍受(比如金融)
  7. 可以通过其他机制获取数据源改变的消息,在通过代码调用IMemoryCache的Set方法更新缓存。

ASP.NET Core 缓存穿透

什么是缓存穿透
String cacheKey-“Book”+id;//缓存键

Book? b=memCache.Get<Book?>(cachekey);
If(b==null) //如果缓存中没有数据

//查询数据库,然后写入缓存
b=awati dbCtx.Books.FindAsync(id);
memCache.Set(cacheKey,b);
 

缓存穿透的解决方案

  1. 解决方法:把“查不到”也当成一个数据放入缓存。
  2. 我们用GetIOrCreateAsync方法即可,因为它会把null值也当成合法的缓存值
String cacheky="Book"+id;
Var book=await memCache.GetOrCreateAsync(cachkey,async=>)
 
Var b= await dbCtx.Books.FindAsync(id);
Logger.LogInformation("数据库查询:0",b==null?"为空":"不为空");
Return b;

Logger.LogInformation("DEMO5执行结束查询:0",b==null?"为空":"不为空");
        [HttpGet]
        public async Task<ActionResult<Book?>> GetBookById(long id) 
        
            Book? result = MyDbContext.GetById(id);
            Console.WriteLine($"开始执行GetBookById,id=id");
            Book? b=await memoryCache.GetOrCreateAsync("Book" + id,
                async (e) => 
 
                    Console.WriteLine($"缓存里没有找到,到数据库中查一查,id=id");
                    //e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);//缓存有效期10秒
                    //e.SlidingExpiration = TimeSpan.FromSeconds(10);
                    Book? d= await MyDbContext.GetByIdAsync(id);
                    Console.WriteLine("从数据库中查询的结果是"+(d==null?"null":d));
 
                    return d;
            );
            Console.WriteLine($"GetOrCreateAsync结果b");
            if (b == null)
            
                return NotFound($"找不到id=id的书");
            
            else 
            
                return b;   
            
        
 

ASP.NETCore缓存雪崩的问题

缓存雪崩
1.缓存项集中过期引起缓存雪崩
2.解决办法:在基础过期时间之上,再加一各随机的过期时间。
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(Random.Shared.Next(10,15));
//过期随机时间

分布式缓存

分布式缓存服务器
1.常用的分布式缓存服务器Redis,Memcached等
2.NET Core中提供了同意的分布式缓存服务器的操作接口IDistributedCache,用法和内存缓存类似。
3.分布式缓存和内存缓存的区别:缓存之的类型为Byte[],需要我们进行类型转换,也提供了一些按照string类型存取缓存值的扩展方法。
用什么做缓存服务器
1.用SQLServer做缓存性能并不好
2.,Memcached是缓存专用,性能非常高,但是集群,高可用等方法比较弱,而且有"缓存键的最大长度为250字节"等限制。可以按照EnyimMemcachedCore这个第三方NuGet包。
3.Redis不局限于缓存,Redis做缓存服务器比Memcached性能稍差,但Redis的高可用,集群等方便非常强大,适合在数据量大,高可用性等场合使用

以上是关于在asp.net中如何管理cache的主要内容,如果未能解决你的问题,请参考以下文章

如何在 asp.net mvc 3 应用程序中管理视图管理

如何在 ASP.NET MVC 应用程序中管理和部署架构(使用 NHibernate)

如何在asp.net mvc 5中批准或拒绝管理员批准的用户端记录

如何在asp.net c#中处理数据库用户

如何在我的 ASP.NET 项目中通过 Revox 设置 Pages 管理面板仪表板模板?

如何在asp.net mvc中使用单独的表管理两种用户?