.NET 6开发之实现缓存过程详解

 更新时间:2022年01月08日 16:24:15   作者:CODE4NOTHING  
有的时候为了减少客户端请求相同资源的逻辑重复执行,我们会考虑使用一些缓存的方式。这篇文章主要就介绍了在.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<ApiResponse<List<TodoListBriefDto>>> Get()
{
    return ApiResponse<List<TodoListBriefDto>>.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<ApiResponse<List<TodoListBriefDto>>> Get()
{
    return ApiResponse<List<TodoListBriefDto>>.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来做缓存也是可以的。

总结

在本文中我们主要演示了如何借助框架的缓存机制来实现请求资源的缓存,尽管在缓存校验的实现上,官方提供的库目前来看并没有能很好地完成功能以外,对于我们基本的使用场景来说已经够用了。下一篇文章我们来看怎么实现接口的限流。

参考资料

1.Varnish

2.Apache Traffic Server

3.Squid

到此这篇关于.NET 6开发之实现缓存过程详解的文章就介绍到这了,更多相关.NET 6实现缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • asp.net生成静态页并分页+ubb

    asp.net生成静态页并分页+ubb

    编程思路生成静态页其实只有内容部分是需要分页的,生成静态页并分页的步骤是在textarea或者在线编辑器中,需要分页的地方就插入一个[ page ]
    2008-10-10
  • WPF图形解锁控件ScreenUnLock使用详解

    WPF图形解锁控件ScreenUnLock使用详解

    这篇文章主要为大家详细介绍了WPF图形解锁控件ScreenUnLock的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • 浅谈Main方法的参数

    浅谈Main方法的参数

    本文主要对Main方法的参数通过案例分析进行介绍,具有很好的参考价值,需要的朋友一起来看下吧
    2016-12-12
  • asp.net gridview指定某一列滚动

    asp.net gridview指定某一列滚动

    gridview指定某一列滚动的asp.net实现代码。
    2009-11-11
  • .NET使用YARP根据域名转发实现反向代理

    .NET使用YARP根据域名转发实现反向代理

    这篇文章介绍了.NET使用YARP根据域名转发实现反向代理的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-09-09
  • ASP.net WebAPI跨域调用问题的解决方法

    ASP.net WebAPI跨域调用问题的解决方法

    在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案。下面这篇文章主要给大家介绍了关于ASP.net WebAPI跨域调用问题的解决方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2018-03-03
  • 高效.NET脏字过滤算法与应用实例

    高效.NET脏字过滤算法与应用实例

    这篇文章主要介绍了高效.NET脏字过滤算法与应用方法,结合实例形式分析了.NET字符串过滤操作相关技巧,需要的朋友可以参考下
    2016-08-08
  • ASP.NET清空缓存时遇到的问题简析

    ASP.NET清空缓存时遇到的问题简析

    本文将为大家介绍的是ASP.NET网站清空缓存时遇到的问题,主要是基于ObjectDataSource读取数据位置的问题,希望对大家有所帮助。
    2015-10-10
  • .NET CPU爆高事故事故分析某供应链WEB网站

    .NET CPU爆高事故事故分析某供应链WEB网站

    这篇文章主要为大家介绍了.NET CPU爆高事故事故分析某供应链WEB网站,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • ASP.NET也像WinForm程序一样运行的实现方法

    ASP.NET也像WinForm程序一样运行的实现方法

    我们今天要谈到的是让ASP.NET的程序也像WinForm一样的运行,这样就不需要安装IIS或者Visual Studio这样的特定环境了
    2012-01-01

最新评论