千家信息网

.NET 6开发中怎么实现缓存

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,小编今天带大家了解.NET 6开发中怎么实现缓存,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着小编一起深入学习
千家信息网最后更新 2024年11月11日.NET 6开发中怎么实现缓存

小编今天带大家了解.NET 6开发中怎么实现缓存,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着小编一起深入学习".NET 6开发中怎么实现缓存"的知识吧。


    需求

    有的时候为了减少客户端请求相同资源的逻辑重复执行,我们会考虑使用一些缓存的方式,在.NET 6中,我们可以借助框架提供的中间件来实现请求资源的缓存。

    目标

    实现请求结果的缓存。

    原理与思路

    对于在.NET6中实现缓存,我们可以使用响应缓存中间件ResponseCaching来实现,同时可以使用Marvin.Cache.Headers来为我们提供更多的缓存相关的属性。

    实现

    使用原生ResponseCaching实现缓存

    既然是中间件,我们便在Program中引入:

    Program.cs

    // 省略其他...// 配置缓存中间件builder.Services.AddResponseCaching();builder.Services.AddControllers();// ...// 使用缓存中间件app.UseResponseCaching();app.MapControllers();

    在使用方法上,有几种方式可以实现配置:1)进行全局的配置,应用于所有添加了相同ProfileNameResponseCache的Controller响应;2)对单个Controller/Action进行配置,应用于当前作用的Controller/Action;3)全局配置后,由单个Controller/Action覆盖全局配置。我们会演示1)和3)的场景。

    我们准备使用获取所有TodoLists的接口进行演示。

    先看如何进行全局配置:

    Program.cs

    // 省略其他...builder.Services.AddControllers(options =>{    options.CacheProfiles.Add("60SecondDuration", new CacheProfile { Duration = 60 });});

    验证1: 全局配置Caching

    首先给我们要进行验证的Action添加属性:

    TodoListController.cs

    // 省略其他...[HttpGet][ResponseCache(CacheProfileName = "60SecondDuration")]public async Task>> Get(){    return ApiResponse>.Success(await _mediator.Send(new GetTodosQuery()));}

    启动Api项目,第一次执行获取TodoLists的请求:

    请求

    响应

    响应头中多了一个cache-control字段用于指明缓存的类型(public)以及过期时间为60s:

    如果你是使用Postman或者Insomia发送的请求,那么在过期前再次发起相同请求的返回头中会再多出一个Age字段,用于表明该资源当前缓存了多少秒(Hoppscotch我没找到可以在哪里设置,所以下面的截图是来自Insomia,如果有哪位老哥知道的可以教一下):

    同时如果观察日志的话会发现,第二次请求并没有实际执行SQL语句,这也证明了第二次请求的返回来自缓存:

    如果间隔60s以上我们再去发送相同的请求,会发现日志中是这样的:

    可以看到缓存已经失效了,此时需要重新向数据库查询返回数据,并将这次请求结果缓存起来。

    验证2: 单个Action覆盖全局配置

    我们还是使用这个接口,但是修改一下属性:

    TodoListController.cs

    [HttpGet][ResponseCache(Duration = 120)]public async Task>> Get(){    return ApiResponse>.Success(await _mediator.Send(new GetTodosQuery()));}

    重新启动Api项目,第一次执行获取TodoLists的请求,请求和验证1相同,我们来看响应中的变化:

    响应

    可以看到失效时间已经变为120s了,其他不再一一验证。

    使用Marvin.Cache.Headers实现更多缓存功能

    在缓存中还有一个问题是,如果判断缓存的数据内容已经变化,就需要去获取最新的响应而不是直接从缓存中取值。这是借助缓存校验来完成的,而常使用的方式是通过Etag实现。示意的过程如下:

    如果首次请求资源,API会在响应头中添加EtagLast-Modified字段:

    当客户端再次请求资源时,由于缓存自身是不知道资源有没有被修改,所以缓存会携带If-None-Match字段(和客户端收到的Etag值相等)和If-Modified-Since字段(和客户端收到的Last-Modified值相等)到API端,如果校验发现资源没有发生修改,那么API端无需重新获取资源,直接返回304字段(NotModifed)给缓存,缓存给客户端返回值。如果校验发现资源发生了修改,那么API将会返回新的结果。

    我们给Api项目添加Nuget包Marvin.Cache.Headers,来实现此功能。

    首先向Program中添加服务以及引入中间件:

    Program.cs

    builder.Services.AddResponseCaching();builder.Services.AddHttpCacheHeaders(    expirationOptions =>    {        expirationOptions.MaxAge = 180;        expirationOptions.CacheLocation = CacheLocation.Private;    },    validateOptions =>    {        validateOptions.MustRevalidate = true;    });// 省略其他...app.UseResponseCaching();app.UseHttpCacheHeaders();

    同时我们需要移除之前添加的ResponseCache属性,因为新引入的库已经帮我们完成了,当然我们也可以通过以下方式覆盖全局配置:

    [HttpCacheExpiration(CacheLocation = CacheLocation.Public, MaxAge = 60)][HttpCacheValidation(MustRevalidate = false)]

    覆盖规则和框架内置的规则是一致的,我不会继续演示。

    验证3: 缓存校验

    请求仍然是获取所有的TodoLists

    响应

    我们暂时只关注响应头:

    如果在缓存失效前我们添加了一个新的TodoList,在请求头中添加If-None-Match=53154EEFAE230D733827DBDE49B42AF9再执行获取请求:

    可以看到在失效时间到期之内,Etag值已经发生了变化,校验表明资源已经改变,需要重新获取。

    如果我们再次获取相同的资源,会得到304返回:

    一点扩展

    但是如果我们仔细观察和思考就会发现,框架在实现缓存校验上存在两个问题:

    1. If-None-Match头字段是我们手动添加模拟的,这本应该由缓存中间件来完成;

    2. 在响应304的情况下,实际上是没有返回响应体的,即缓存中未修改的资源没有返回;

    这两个问题是由框架内建的ResponseCaching库导致的,可以认为它没有正确地实现缓存校验机制。为此我们有一些替代方案可供参考:

    Varnish

    Apache Traffic Server

    Squid

    当然使用专门的CDN来做缓存也是可以的。

    感谢大家的阅读,以上就是".NET 6开发中怎么实现缓存"的全部内容了,学会的朋友赶紧操作起来吧。相信小编一定会给大家带来更优质的文章。谢谢大家对网站的支持!

    0