0%

AspNetCore——缓存

缓存

  • 缓存是一种通过存储资源的备份,在请求时返回资源备份的技术。使用缓存能够减少服务器的压力和网络带宽,并减少客户端延迟。AspNetCore支持多种形式的缓存,Http的、内存的、分布式的,还提供了响应缓存中间件。

过期模型

捕获1

验证模型

捕获2

Http缓存

缓存类型

  • Http有三种缓存:
    1. 客户端/浏览器缓存,存在于客户端,并且是私有的。
    2. 网关缓存,它是共享的缓存,位于服务器端,所有的客户端都会共享这个缓存,它的别名还有反向代理服务器缓存,HTTP加速器等。
    3. 代理缓存,位于网络上,是共享的,它既不位于客户端,也不位于服务器,类似于云存储或CDN。这种缓存经常被大型企业或ISP使用,用来服务大规模的用户。

捕3

缓存相关的消息头

  • Http缓存的形式在Web应用程序的使用非常普遍,它主要是通过与缓存相关的消息头实现的,这些消息头指明了缓存行为(是否使用缓存、缓存的有效时间等),主要包括Cache-Control和Expires。

  • 缓存相关的响应头的含义:

    捕获

HTTP1.1 200 OK
Cache-Control:public, max-age=60

  • Expires消息头也可以用来指定缓存的有效时间,但它的值是一个绝对时间

缓存是否有效

  • 上面则表示了该响应支持缓存,并且在任何地方都可以存储该响应,有效时间为60s;当缓存的响应失效后,此时当客户端再次使用资源时,就需要请求服务器,已验证缓存的响应是否有效(是否被改变)。如果有效,则服务器返回304Not Modified状态码,响应中也不会包含任何响应体;如果失效,服务器则返回200OK状态码以及最新资源。

如何验证缓存是否有效

  • 验证缓存资源的方式有两种,一种是Last-Modified,另一种是ETag
    1. Last-Modified的值是资源最后更新的时间,在验证时,需要在请求消息中添加If-Modified-Since消息头,这个值是客户端最近一次收到该资源响应中Last-Modified的值,服务器就可以拿If-Modified-Since的值跟资源最后的更新时间进行比较,如果相同则缓存有效,不同则缓存无效。
    2. ETag可以看作是由服务器为当前资源生成的唯一标识,当客户端在请求验证时需要在消息头中添加If-None-Match,它的值为该资源最近异常从服务器中获得的ETag值。当服务器中资源发生改变时,它的ETag值会更改,所以服务器可以用它来识别缓存的响应是否和资源的最新状态一致。
  • 建议使用ETag,因为Last-Modified是以秒为单位来进行验证的,有可能在一秒内对api进行了多次修改请求,Last-Modified的验证就会失效了。

服务器
HTTP1.1 200OK
Cache-Control:public, max-age=60
ETag: “ead145f”
客户端
GET /api/authors
If-None-Match: “ead145f”
服务器
HTTP1.1 304 NotModified
Cache-Control:public, max-age=60
ETag: “ead145f”

  • 注意,只有请求方法为GET或HEAD,并且服务器返回200时的响应才支持缓存。

让WebApi支持Http缓存

  • 在AspNetCore中实现Http缓存需要提供两样东西:
    1. 表明资源的缓存信息,即响应的消息头,它由ResponseCache特性来支持,它只设置了响应消息头项的信息,并没有缓存任何的数据。
    2. 缓存的存储器,它可以由response cache中间件来支持,也可以使用其他的存储器,比如redis等等。
  • 在想要被缓存的Action上添加ResponseCache特性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// 添加响应缓存,有效期为60秒,响应缓存有三种类型:
/// Any:对应Cache-Control的值为public,表示响应可以被任何对象缓存
/// Client:对应Cache-Control的值为private,表示响应只能为单个用户缓存,不能作为共享缓存
/// None:对应Cache-Control的值为no-cache,表示必须到服务器验证才能使用缓存
/// 默认是public
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
[HttpGet("{authorId}", Name = nameof(GetAuthorAsync))]
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
public async Task<ActionResult<AuthorDto>> GetAuthorAsync(Guid authorId)
{
var author = await _repositoryWrapper.Author.GetByIdAsync(authorId);
if (author==null)
{
return NotFound();
}
var authorDto = _mapper.Map<AuthorDto>(author);
return Ok(authorDto);
}
  • 然后在Startup.cs添加缓存的中间件,在ConfigureServices方法添加以下代码
1
services.AddResponseCaching();
  • 在Configure方法添加以下代码
1
2
//要在UseRouting之前
app.UseResponseCaching();

添加缓存的Profile(属性的模板)

1
2
3
4
5
6
7
8
9
services.AddControllers(config =>
{
config.ReturnHttpNotAcceptable = true;
//添加缓存的设置模板
config.CacheProfiles.Add("120sCacheProfile", new CacheProfile
{
Duration = 120
});
});
  • 在AuthorController添加全局的Profile,除了上面GetAuthorAsync方法外,其他方法的缓存有效期就都是120s
1
2
3
4
[Route("api/authors")]
[ApiController]
[ResponseCache(CacheProfileName = "120sCacheProfile")]
public class AuthorController:ControllerBase{...}

缓存请求响应的Cache-Control的指令

  • 请求

捕获6

  • 响应

捕获5

让WebApi支持ETag

  • 一般WebApi不会只对过期模型进行配置,还会对验证模型进行配置,验证模型分为强验证器(ETag)和弱验证器(Last-Modified);实现ETag需要添加一个依赖Marvin.Cache.Headers
  • 在Startup.cs添加全局的过期模型和验证模型的配置:
1
services.AddHttpCacheHeaders();
  • 然后在Configure方法中添加app.UseHttpCacheHeaders();
  • 使用Postman进行请求后,可以看到Header如下:HttpCacheHeaders帮我们自动加上了强验证器和弱验证器。

捕获7

全局配置

  • 可以在AddHttpCacheHeaders方法参数中对过期模型和验证模型进行配置
1
2
3
4
5
6
7
8
9
10
//添加过期模型和验证模型的配置
//在这里的配置是全局的配置
services.AddHttpCacheHeaders(expires =>
{
expires.CacheLocation = CacheLocation.Private;
expires.MaxAge = 60;
}, validation =>
{
validation.MustRevalidate = true;
});
  • 请求结果是这样的,由于缓存的类型为private,Api并不会帮我们保存缓存,所以就没有Age这个项。

捕获8

  • Vary项的含义是:当请求的Accept的媒体类型和缓存中的数据类型不一致的时候,响应并不会从缓存返回,而会去请求Api的数据。

在Action或Controller级别上的配置

  • 在GetAuthorsAsync这个Action上添加配置
1
2
3
4
5
6
7
[HttpGet(Name = nameof(GetAuthorsAsync))]
//过期模型配置
[HttpCacheExpiration(CacheLocation = CacheLocation.Private, MaxAge = 120)]
//验证模型配置
[HttpCacheValidation(MustRevalidate = false)]
public async Task<ActionResult<IEnumerable<AuthorDto>>> GetAuthorsAsync
([FromQuery] AuthorResourceParameters parameters){...}
  • Controller级别的类似,这里就不写了。

测试ETag(验证器)

  • 先请求一次,然后可以在响应头中获取到ETag,然后在请求头中添加If-None-Match,值为ETag的值,请求。返回结果可以看到304状态码,没有响应体,同时带回最新的ETag。

捕获10

  • 然后我们进行一次Put请求,修改资源成功

捕获11

  • 再用第一次的Get请求加If-None-Match项请求相同的数据,返回了200OK,并且返回了最新的ETag。

捕获12


学习资源:B站杨旭

-------------本文结束感谢您的阅读-------------