ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

服务器端响应缓存、内存缓存、分布式缓存

2022-08-04 01:02:49  阅读:194  来源: 互联网

标签:缓存 服务器端 过期 await LogInformation memCache logger 分布式


服务器端响应缓存、内存缓存、分布式缓存

Asp.Net Core服务器端响应缓存

浏览器缓存只能作用于自己,服务器端响应缓存可以作用于所有向服务器发起的请求

1:如果ASP.NET Core中安装了“响应缓存中间件” ,那么ASP.NET Core不仅会继续根据[ResponseCache]设置来生成cache-control响应报文头来设置客户端缓存,而且服务器端也会按照[ResponseCache]的设置来对响应进行服务器端缓存。和客户端端缓存的区别?来自多个不同客户端的相同请求。

2:“响应缓存中间件”的好处:对于来自不同客户端的相同请求或者不支持客户端缓存的客户端,能降低服务器端的压力。

3:用法:app.MapControllers()之前加上app.UseResponseCaching()。请确保app.UseCors()写到app.UseResponseCaching()之前。默认不启用

4:请求头如果带有 send control:no-cache 服务器缓存会禁用(很容易无效 鸡肋)无法解决恶意请求给服务器带来的压力。

5:服务器端响应缓存还有很多限制,包括但不限于:响应状态码为200的GET或者HEAD响应才可能被缓存;报文头中不能含有Authorization、Set-Cookie等。

内存缓存

1:把缓存数据放到应用程序的内存。内存缓存中保存的是一系列的键值对,就像Dictionary类型一样。

2:内存缓存的数据保存在当前运行的网站程序的内存中,是和进程相关的。因为在Web服务器中,多个不同网站是运行在不同的进程中的,因此不同网站的内存缓存是不会互相干扰的,而且网站重启后,内存缓存中的所有数据也就都被清空了。

如何使用
Program:
 builder.Services.AddMemoryCache();//注入对应的服务
测试Controller
     //注入要使用的服务:注入IMemoryCache接口,查看接口的方法:TryGetValue、Remove、Set、GetOrCreate、GetOrCreateAsync
  private readonly ILogger<Test1Controller> logger;
     private readonly MyDbContext dbCtx;
     private readonly IMemoryCache memCache;
     public Test1Controller(MyDbContext dbCtx, IMemoryCache memCache, ILogger<Test1Controller> logger)
    {
         this.dbCtx = dbCtx;
         this.memCache = memCache;
         this.logger = logger;
    }
 ​
  [HttpGet]
     public async Task<Book[]> GetBooks()
    {
         logger.LogInformation("开始执行GetBooks");
         var items = await memCache.GetOrCreateAsync("AllBooks", async (e) =>
        {
             logger.LogInformation("从数据库中读取数据");
             return await dbCtx.Books.ToArrayAsync();
        });
         logger.LogInformation("把数据返回给调用者");
         return items;
    }
缓存的过期时间策略:

1:上面的例子中的缓存不会过期,除非重启服务器。

2:解决方法:在数据改变(非查询操作)的时候调用Remove或者Set来删除或者修改缓存(优点:及时);过期时间(只要过期时间比较短,缓存数据不一致的情况也不会持续很长时间。)

3:两种过期时间策略:绝对过期时间、滑动过期时间。它们分别是什么?

绝对过期时间
         logger.LogInformation("开始执行Demo1:" + DateTime.Now);
         var items = await memCache.GetOrCreateAsync("AllBooks", async (e) => {
             e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
             logger.LogInformation("从数据库中读取数据");
             return await dbCtx.Books.ToArrayAsync();
        });
         logger.LogInformation("Demo1执行结束");
滑动过期时间
 //使用滑动过期时间策略,如果一个缓存项一直被频繁访问,那么这个缓存项就会一直被续期而不过期。    
  logger.LogInformation("开始执行Demo2:" + DateTime.Now);
     var items = await memCache.GetOrCreateAsync("AllBooks2", async (e) => {
         e.SlidingExpiration = TimeSpan.FromSeconds(10);
         logger.LogInformation("Demo2从数据库中读取数据");
         return await dbCtx.Books.ToArrayAsync();
    });
     logger.LogInformation("Demo2执行结束");
两种过期机制混合使用
 //可以对一个缓存项同时设定滑动过期时间和绝对过期时间,并且把绝对过期时间设定的比滑动过期时间长,这样缓存项的内容会在绝对过期时间内随着访问被滑动续期,但是一旦超过了绝对过期时间,缓存项就会被删除
  logger.LogInformation("开始执行Demo3:" + DateTime.Now);
         var items = await memCache.GetOrCreateAsync("AllBooks3", async (e) => {
             e.SlidingExpiration = TimeSpan.FromSeconds(10);
             e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
             logger.LogInformation("Demo3从数据库中读取数据");
             return await dbCtx.Books.ToArrayAsync();
        });
         logger.LogInformation("Demo3执行结束");
总结:

(1):绝对过期时间 是指设置一段时间,超过这个时间,缓存过期例如设置三秒那么这个缓存三秒后就会失效,再请求则是从数据库获取数据。

(2):滑动过期时间:设置三秒钟,如果三秒内请求了,这个缓存则不会失效,并且续命三秒,然后三秒后再请求,此时缓存已经失效,则是从数据库获取数据。(类似奶茶店做活动,十秒钟喝完一杯,可以续杯,可以无限续杯,但是得在这个规定的时间喝完)

(3):混合使用,则是在绝对过期时间之前,可以续命,(类似奶茶店做活动,十秒钟喝完一杯,可以续杯,但是不可以无限续杯,最多只能十分钟内)

(4):一般是使用绝对过期时间方式或者混合使用,因为第二种方式如果数据一直被访问,缓存不会失效

任何事物都有两面性:享受了缓存带来的好处,必然这个技术也有他的缺点:

(1)缓存穿透问题
 string cacheKey = "Book" + id;//缓存键
 Book? b = memCache.Get<Book?>(cacheKey);
 if(b==null)//如果缓存中没有数据
 {
  //查询数据库,然后写入缓存
  b = await dbCtx.Books.FindAsync(id);
  memCache.Set(cacheKey, b);
 }

上述代码正常逻辑没什么问题,但是如果是恶意请求,那么会一直访问一个不存在的id,那么每次都是直接访问数据库,这个缓存形同虚设。所以当查询这个不存在的id的时候,把它的结果null缓存起来 {key:id,value:null},下次再访问则是从缓存中获取数据。避免了缓存穿透。之前的代码使用的GetOrCreateAsync()方法,默认解决了缓存穿透,所以默认推荐大家使用这个方法

         logger.LogInformation("开始执行Demo5");
         string cacheKey = "Book" + id;
         var book = await memCache.GetOrCreateAsync(cacheKey, async (e) => {
             var b = await dbCtx.Books.FindAsync(id);
             logger.LogInformation("数据库查询:{0}", b == null ? "为空" : "不为空");
             return b;
        });
         logger.LogInformation("Demo5执行结束:{0}", book == null ? "为空" : "不为空");
         return book;
总结:

1:解决方法:把“查不到”也当成一个数据放入缓存。

2:我们用GetOrCreateAsync方法即可,因为它会把null值也当成合法的缓存值。

(2)缓存雪崩

1:缓存项集中过期引起缓存雪崩。

2:解决方法:在基础过期时间之上,再加一个随机的过期时间

缓存的相关问题面试常问热点redis参考:https://blog.csdn.net/qq_41071876/article/details/120076924

分布式缓存

1、常用的分布式缓存服务器有Redis、Memcached等。

2、.NET Core中提供了统一的分布式缓存服务器的操作接口IDistributedCache,用法和内存缓存类似。

3、分布式缓存和内存缓存的区别:缓存值的类型为byte[],需要我们进行类型转换,也提供了一些按照string类型存取缓存值的扩展方法。

 

 

4、用SQLServer做缓存性能并不好。

5、Memcached是缓存专用,性能非常高,但是集群、高可用等方面比较弱,而且有“缓存键的最大长度为250字节”等限制。可以安装EnyimMemcachedCore这个第三方NuGet包。

6、Redis不局限于缓存,Redis做缓存服务器比Memcached性能稍差,但是Redis的高可用、集群等方面非常强大,适合在数据量大、高可用性等场合使用

如何使用:

1、NuGet安装Microsoft.Extensions.Caching.StackExchangeRedis

 builder.Services.AddStackExchangeRedisCache(options =>
 {
     options.Configuration = "localhost";
     options.InstanceName = "demo1_";//避免混乱,让key 具有识别性
 });
如何使用:具体的方法看IDistributedCache接口
  private readonly IDistributedCache distCache;
  public Test1Controller(IDistributedCache distCache)
        {
             this.distCache = distCache;
        }
 ​
  [HttpGet]
  public string Now()
  {
  string s = distCache.GetString("Now");
  if (s == null)
  {
  s = DateTime.Now.ToString();
  var opt = new DistributedCacheEntryOptions();
  opt.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
  distCache.SetString("Now", s, opt);
  }
  return s;
  }
注意事项:

IQueryable、IEnumerable等类型可能存在着延迟加载的问题,如果把这两种类型的变量指向的对象保存到缓存中,在我们把它们取出来再去执行的时候,如果它们延迟加载时候需要的对象已经被释放的话,就会执行失败。因此缓存禁止这两种类型。

 

本文内容大部分都为杨中科老师《ASP.NET Core技术内幕与项目实战》一书中内容,此文只是做学习记录,如有侵权,联系立马删除。

标签:缓存,服务器端,过期,await,LogInformation,memCache,logger,分布式
来源: https://www.cnblogs.com/firstsaofan/p/16549271.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有