.NET Core缓存

目录

缓存的概念

客户端响应缓存

cache-control

服务器端响应缓存

内存缓存(In-memory cache)

用法

GetOrCreateAsync

缓存过期时间策略

缓存的过期时间

解决方法:

两种过期时间策略:

绝对过期时间

滑动过期时间

两种过期时间混用

总结

 封装内存缓存操作的帮助类

使用


缓存的概念

缓存(Caching)是用来保存数据的区域,从缓存区域读取数据的速度比从数据源读取数据的速度快很多,从数据源获取数据后,我们可以把数据保存到缓存中,下次再需要获取同样数据时,可以直接从缓存中获取之前保存的数据,是系统优化中简单又有效的工具,投入小收效大。数据库中的索引等简单有效的优化功能本质上都是缓存。

  1. 缓存命中:从缓存中获取了要获取的数据
  2. 缓存命中率:多次请求中,命中的请求占全部请求的百分比
  3. 缓存数据不一致:数据源中数据保存到缓存后,发生了变化
  4. 多级缓存:在Web开发中,存在多级缓存,浏览器存在“浏览器端缓存”,网关节电服务器存在“节点缓存”,,Web服务器上可能存在“服务器端缓存”,只要在任何一个节点上命中缓存,请求就会直接返回,而不会继续向后传递。

客户端响应缓存

cache-control

  1. RFC7324是HTTP协议中对缓存进行控制的规范,其中重要的是cache-control这个响应报文头。服务器如果返回cache-control:max-age=60,则表示服务器指示浏览器端“可以缓存这个响应内容60秒”。
  2. 我们只要给需要进行缓存控制的控制器的操作方法添加ResponseCacheAttribute这个Attribute,ASP.NET Core会自动添加cache-control报文头。
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
    [HttpGet]
    [ResponseCache(Duration = 20)]
    public DateTime Now()
    {
        return DateTime.Now;
    }
}

服务器端响应缓存

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

缺点:

  1. 无法解决恶意请求给服务器带来的压力。
  2. 服务器端响应缓存还有很多限制,包括但不限于:响应状态码为200的GET或者HEAD响应才可能被缓存;报文头中不能含有Authorization、Set-Cookie等。
  3. 可以采用内存缓存、分布式缓存等。

由于服务器端响应缓存开发调试的麻烦以及过于苛刻的限制,因此除非开发人员能够灵活掌握并应用,否则不建议启用“响应缓存中间件”。对于只需要进行客户端响应缓存处理的操作方法,标注ResponseCache即可,如果还需要在服务器端进行缓存处理,建议采用ASP.NET Core提供的内存缓存、分布式缓存等机制来编写程序。

内存缓存(In-memory cache)

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

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

用法

  1. 添加服务:builder.Services.AddMemoryCache()
  2. 注入IMemoryCache接口,查看接口的方法:TryGetValue、Remove、Set、GetOrCreate、GetOrCreateAsync

GetOrCreateAsync

GetOrCreateAsync<TItem>(object key, Func<ICacheEntry, Task<TItem>> factory)

获取缓存键为key的缓存值,方法的返回值为获取的缓存值,如果缓存中没有缓存键为key的缓存值,则调用factory指向的回调从数据源获取数据,把获取的数据作为缓存值保存到缓存中,并且把获取的数据作为方法的返回值。

Program.cs
//添加内存缓存服务
services.AddMemoryCache();
//添加DbContext
services.AddScoped<MyDbContext>();

public record Book(long Id, string Name);

public class BookConfig : IEntityTypeConfiguration<Book>
{
    public void Configure(EntityTypeBuilder<Book> builder)
    {
        builder.ToTable("T_Books");
    }
}

public class MyDbContext:DbContext
{
    DbSet<Book> books {  get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.UseSqlServer("Server=.;Database=demo;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=true;");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
    }
}
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
    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?>> GetById(int id)
    {
        using (MyDbContext ctx = new MyDbContext())
        {
            //没加内存缓存
            //var result = await ctx.Set<Book>().SingleOrDefaultAsync(o => o.Id == id);
            //if (result == null)
            //{
            //    return NotFound($"找不到id={id}的书");
            //}
            //else
            //{
            //    return result;
            //}

            //内存缓存
            logger.LogInformation($"开始执行GetBookById,id={id}");
            Book? b = await memoryCache.GetOrCreateAsync("Book" + id, async (e) =>
            {
                logger.LogInformation($"缓存没找到,到数据库查询,id={id}");
                return await ctx.Set<Book>().SingleOrDefaultAsync(o => o.Id == id);
            });
            logger.LogInformation($"GetBookById结果:{b}");
            if (b == null)
            {
                return NotFound($"找不到id={id}的书");
            }else
            {
                return Ok(b);
            }
        }
    }
}

 查询两次结果

缓存过期时间策略

缓存的过期时间

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

解决方法:

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

两种过期时间策略:

GetOrCreateAsync()方法的回调方法中有一个ICacheEntry类型的参数,通过ICacheEntry对当前的缓存项做设置。

绝对过期时间

设置缓存完的指定时间后,缓存项被清除。

AbsoluteExpirationRelativeToNow:用来设定缓存项的绝对过期时间。

Book? b = await memoryCache.GetOrCreateAsync("Book" + id, async (e) =>
{
    logger.LogInformation($"缓存没找到,到数据库查询,id={id}");
    //缓存有效期10s
    e.AbsoluteExpirationRelativeToNow=TimeSpan.FromSeconds(10);
    return await ctx.Set<Book>().SingleOrDefaultAsync(o => o.Id == id);
});
滑动过期时间

设置缓存完的指定时间后,如果对应的缓存数据没有被访问,缓存项被清除,如果在指定时间内,被访问一次,则缓存项的过期时间会自动续期。

Book? b = await memoryCache.GetOrCreateAsync("Book" + id, async (e) =>
{
    logger.LogInformation($"缓存没找到,到数据库查询,id={id}");
    e.SlidingExpiration=TimeSpan.FromSeconds(10);
    return await ctx.Set<Book>().SingleOrDefaultAsync(o => o.Id == id);
});
两种过期时间混用

使用滑动过期时间策略,如果一个缓存项一直被频繁访问,那么这个缓存项就会一直被续期而不过期。可以对一个缓存项同时设定滑动过期时间和绝对过期时间,并且把绝对过期时间设定的比滑动过期时间长,这样缓存项的内容会在绝对过期时间内随着访问被滑动续期,但是一旦超过了绝对过期时间,缓存项就会被删除。

总结

  1. 无论用那种过期时间策略,程序中都会存在缓存数据不一致的情况。部分系统(博客等)无所谓,部分系统不能忍受(比如金融)。
  2. 可以通过其他机制获取数据源改变的消息,再通过代码调用IMemoryCache的Set方法更新缓存。

 封装内存缓存操作的帮助类

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

使用

Install-Package Zack.ASPNETCore
NETBookMaterials/最后大项目代码/YouZack-VNext/Zack.ASPNETCore/MemoryCacheHelper.cs at main · yangzhongke/NETBookMaterialshttps://github.com/yangzhongke/NETBookMaterials/blob/main/%E6%9C%80%E5%90%8E%E5%A4%A7%E9%A1%B9%E7%9B%AE%E4%BB%A3%E7%A0%81/YouZack-VNext/Zack.ASPNETCore/MemoryCacheHelper.cshttps://github.com/yangzhongke/NETBookMaterials/blob/main/%E6%9C%80%E5%90%8E%E5%A4%A7%E9%A1%B9%E7%9B%AE%E4%BB%A3%E7%A0%81/YouZack-VNext/Zack.ASPNETCore/MemoryCacheHelper.cshttps://github.com/yangzhongke/NETBookMaterials/blob/main/%E6%9C%80%E5%90%8E%E5%A4%A7%E9%A1%B9%E7%9B%AE%E4%BB%A3%E7%A0%81/YouZack-VNext/Zack.ASPNETCore/MemoryCacheHelper.cshttps://github.com/yangzhongke/NETBookMaterials/blob/main/%E6%9C%80%E5%90%8E%E5%A4%A7%E9%A1%B9%E7%9B%AE%E4%BB%A3%E7%A0%81/YouZack-VNext/Zack.ASPNETCore/MemoryCacheHelper.cs

services.AddMemoryCache();
services.AddScoped<IMemoryCacheHelper,MemoryCacheHelper>();

public TestController(IMemoryCache memoryCache, MyDbContext ctx, MemoryCacheHelper memoryCacheHelper)
{
    this.memoryCache = memoryCache;
    this.ctx = ctx;
    this.memoryCacheHelper = memoryCacheHelper;
}

[HttpGet]
public async Task<ActionResult<Book?>> Test(long id)
{
    var b = memoryCacheHelper.GetOrCreateAsync("Book" + id, async (e) =>
    {
        return await ctx.Set<Book>().SingleOrDefaultAsync(o => o.Id == id);
    }, 10);
    if (b.Result == null)
    {
        return NotFound("不存在");
    }
    else
    {
        return Ok(b.Result);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何从客观角度批判性阅读分析博客

此文仅以个人博客为例&#xff0c;大量阅读朋友反馈给我的交流让我得知他们所理解我的博客所表达的意思并非我所想表达的&#xff0c;差异或大或小&#xff0c;因人而异。 观点与事实 只有从客观角度反复批判性阅读和分析&#xff0c;才能逐渐清晰观点和事实。 观点不等于事实…

【力扣】49.字母异位词分组

AC截图 题目 思路 由于互为字母异位词的两个字符串包含的字母相同&#xff0c;因此对两个字符串分别进行排序之后得到的字符串一定是相同的&#xff0c;故可以将排序之后的字符串作为哈希表的键。 可以遍历strs&#xff0c;将其中每一个str排序&#xff0c;然后用unodered_ma…

【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!

Day4 迈向高手之路——进一步学习&#xff01; 目录 Day4 迈向高手之路——进一步学习&#xff01;更多的开发板外壳制作 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4…

什么是线性化PDF?

线性化PDF是一种特殊的PDF文件组织方式。 总体而言&#xff0c;PDF是一种极为优雅且设计精良的格式。PDF由大量PDF对象构成&#xff0c;这些对象用于创建页面。相关信息存储在一棵二叉树中&#xff0c;该二叉树同时记录文件中每个对象的位置。因此&#xff0c;打开文件时只需加…

向下调整算法(详解)c++

算法流程&#xff1a; 与⽗结点的权值作⽐较&#xff0c;如果⽐它⼤&#xff0c;就与⽗亲交换&#xff1b; 交换完之后&#xff0c;重复 1 操作&#xff0c;直到⽐⽗亲⼩&#xff0c;或者换到根节点的位置 大家可能会有点疑惑&#xff0c;这个是大根堆&#xff0c;22是怎么跑到…

unity学习25:用 transform 进行旋转和移动,简单的太阳地球月亮模型,以及父子级关系

目录 备注内容 1游戏物体的父子级关系 1.1 父子物体 1.2 坐标关系 1.3 父子物体实际是用 每个gameobject的tranform来关联的 2 获取gameObject的静态数据 2.1 具体命令 2.2 具体代码 2.3 输出结果 3 获取gameObject 的方向 3.1 游戏里默认的3个方向 3.2 获取方向代…

C基础算法与实现

前言 通过业务侧输入需求,使用代码完成。 1.偶数立方和 编写函数求1~100中奇数的平方与偶数的立方的和 1.1代码实现结果 1.2源码示例 #include <stdio.h>// 计算1到100中奇数的平方与偶数的立方的和 int calculateSum() {int sum 0;// 遍历1到100之间的所有数字for (…

基于SSM实现的乡村振兴文化平台系统功能实现十八

一、前言介绍&#xff1a; 1.1 项目摘要 农耕文明是广大群众在几千年的农业生产生活中智慧的结晶&#xff0c;不仅是乡土文化的核心和精髓&#xff0c;还是中华文明的起源和基因。因此&#xff0c;传承和发扬优秀乡村文化&#xff0c;是传承农耕文明的必然要求。 文化振兴是乡…

如何让一个用户具备创建审批流程的权限

最近碰到一个问题&#xff0c;两个sandbox&#xff0c;照理用户的权限应该是一样的&#xff0c;结果开发环境里面我可以左右的做各种管理工作&#xff0c;但是使用change set上传后&#xff0c;另一个环境的同一个用户&#xff0c;没有相对于的权限&#xff0c;权限不足。 当时…

实现B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

解锁维特比算法:探寻复杂系统的最优解密码

引言 在复杂的技术世界中&#xff0c;维特比算法以其独特的魅力和广泛的应用&#xff0c;成为通信、自然语言处理、生物信息学等领域的关键技术。今天&#xff0c;让我们一同深入探索维特比算法的奥秘。 一、维特比算法的诞生背景 维特比算法由安德鲁・维特比在 1967 年提出…

CPU 100% 出现系统中断 怎么解决

CPU 100% 出现系统中断 怎么解决 电脑开机时会掉帧&#xff0c;切换到桌面时就会卡顿&#xff0c;然后打开任务管理器就会看到系统中断的cpu占用率达到100%&#xff0c;过一段时间再打开还是会有显示100%的占用率&#xff0c;这个问题怎么解决&#xff1f; 文章目录 CPU 100% …

Python 梯度下降法(五):Adam Optimize

文章目录 Python 梯度下降法&#xff08;五&#xff09;&#xff1a;Adam Optimize一、数学原理1.1 介绍1.2 符号说明1.3 实现流程 二、代码实现2.1 函数代码2.2 总代码2.3 遇到的问题2.4 算法优化 三、优缺点3.1 优点3.2 缺点 Python 梯度下降法&#xff08;五&#xff09;&am…

labelme_json_to_dataset ValueError: path is on mount ‘D:‘,start on C

这是你的labelme运行时label照片的盘和保存目的地址的盘不同都值得报错 labelme_json_to_dataset ValueError: path is on mount D:,start on C 只需要放一个盘但可以不放一个目录

中间件安全

一.中间件概述 1.中间件定义 介绍&#xff1a;中间件&#xff08;Middleware&#xff09;作为一种软件组件&#xff0c;在不同系统、应用程序或服务间扮演着数据与消息传递的关键角色。它常处于应用程序和操作系统之间&#xff0c;就像一座桥梁&#xff0c;负责不同应用程序间…

玩转大语言模型——配置图数据库Neo4j(含apoc插件)并导入GraphRAG生成的知识图谱

系列文章目录 玩转大语言模型——使用langchain和Ollama本地部署大语言模型 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用GraphRAGOllama构建知识图谱 玩转大语言模型——完美解决Gra…

sizeof和strlen的对比与一些杂记

1.sizeof和strlen的对比 1.1sizeof &#xff08;1&#xff09;sizeof是一种操作符 &#xff08;2&#xff09;sizeof计算的是类型或变量所占空间的大小&#xff0c;单位是字节 注意事项&#xff1a; &#xff08;1&#xff09;sizeof 返回的值类型是 size_t&#xff0c;这是一…

书生大模型实战营6

文章目录 L1——基础岛玩转书生「多模态对话」与「AI搜索」产品MindSearch 开源的 AI 搜索引擎书生浦语 InternLM 开源模型官方的对话类产品书生万象 InternVL 开源的视觉语言模型官方的对话产品在知乎上的提交 L1——基础岛 玩转书生「多模态对话」与「AI搜索」产品 MindSea…

three.js+WebGL踩坑经验合集(6.1):负缩放,负定矩阵和行列式的关系(2D版本)

春节忙完一轮&#xff0c;总算可以继续来写博客了。希望在春节假期结束之前能多更新几篇。 这一篇会偏理论多一点。笔者本没打算在这一系列里面重点讲理论&#xff0c;所以像相机矩阵推导这种网上已经很多优质文章的内容&#xff0c;笔者就一笔带过。 然而关于负缩放&#xf…

Baklib解析内容中台与人工智能技术带来的价值与机遇

内容概要 在数字化转型的浪潮中&#xff0c;内容中台与人工智能技术的结合为企业提供了前所未有的发展机遇。内容中台作为一种新的内容管理和生产模式&#xff0c;通过统一管理和协调各种内容资源&#xff0c;帮助企业更高效地整合内外部数据。而人工智能技术则以其强大的数据…