.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)进行全局的配置,应用于所有添加了相同ProfileName
的ResponseCache
的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会在响应头中添加Etag
和Last-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
返回:
一点扩展
但是如果我们仔细观察和思考就会发现,框架在实现缓存校验上存在两个问题:
If-None-Match
头字段是我们手动添加模拟的,这本应该由缓存中间件来完成;在响应
304
的情况下,实际上是没有返回响应体的,即缓存中未修改的资源没有返回;
这两个问题是由框架内建的ResponseCaching
库导致的,可以认为它没有正确地实现缓存校验机制。为此我们有一些替代方案可供参考:
Varnish
Apache Traffic Server
Squid
当然使用专门的CDN
来做缓存也是可以的。
感谢大家的阅读,以上就是".NET 6开发中怎么实现缓存"的全部内容了,学会的朋友赶紧操作起来吧。相信小编一定会给大家带来更优质的文章。谢谢大家对网站的支持!