Web性能优化-ReponseCaching
2021-01-16 23:13
标签:mamicode repo 服务端 信息 ica 中间件 tco routing bsp Web性能影响因素有多个方面,对应优化方案也有多个,今天聊的是缓存方向。 缓存也包括好多种(程序猿太难了),但概括地分就是服务端缓存和客户端缓存。 今天聊得是客户端缓存-浏览器缓存。 为区分两种缓存的差异,简单多说两句。 服务端缓存最常见、最简单的就是在咱们写的后台业务中加入缓存机制(其他方式的就不展开了 但建议自行了解拓展一下)。 例如MemoryCache、Redis,哪怕最基础Dictionary也可以作为缓存。 用个人通俗的话概括其特点是: 回到浏览器缓存,浏览器缓存实际上也不止一种,但依然不过多展开,只说基于HTTP协议的缓存机制。 没错,是缓存机制。既然是机制,就和服务端的缓存实现有明显区别了。 服务端一般需要写一些额外的逻辑,加入额外的依赖,才能实现目的。但基于HTTP的缓存,可以认为只是做了一些配置,然后按照规范使用就可以了。 先把Demo代码奉上。 Controller部分逻辑 代码比较简单,简单看一下就能懂:提供对一个数据源(Customers)进行增删改查的4个Api,即4个Action。 所以剩下关键就是如何利用这个缓存机制了。 一、启用响应缓存机制 在Startup类中添加以下代码: 二、引入Marvin.Cache.Headers HTTP缓存机制,主要依赖于通过HTTP Header在服务器和客户端段之间进行缓存相关参数、状态的传递。 而这个依赖就是支持从服务端按照协议返回必要的Header,请留意稍后截图中的Header组成。 在Startup类中添加以下代码: 注意事项: 三、服务端的“配置” 缓存机制启用了,Header支持也添加了,剩下就是配置具体的参数了。 在以上代码基础上,为各个参数进行单独配置的示例代码如下: 四、客户端端的“配合” 服务端准备就绪,客户端也需要配合一下。简单说就是遵守协议。HTTP协议怎么规定的,就怎么执行。 步骤1 在Action Get内添加断点,然后启动VS调试。 此时程序会命中该断点。这说明第一次请求进入到了这个API内部。 F5继续执行程序,浏览器(以Chrome为例演示)运行效果如下图。 步骤2 在浏览器通过F5或Ctrl+R刷新页面 此时你会发现,程序并没有再次命中断点。因为浏览器本身支持HTTP缓存,当前缓存已经生效。 此时,你可能会好奇Ctrl+F5强制刷新会怎么样。答案是:会命中断点!试一试,然后记住这个结果。 由于浏览器默默帮我们做了一些事,所以上面的过程并没有暴露出缓存的原理细节。我们改用Postman尝试一下。 步骤3 用Postman请求Get接口 在Postman输入Get接口地址,直接访问。我们得到的响应信息如下图所示,请留意Header的组成。 并且,当我们反复请求该接口时候,会发现断点每次都会命中。因为我们没按协议办事(就是刚才浏览器帮我们办的事),服务器也不知所措。 步骤4 告诉服务器“你可以给我缓存结果” 我们细看以上图中的Header,有几个关键的Key。 之所以缓存没过期但是没有生效,就是因为客户端没有和服务器端沟通好“如何使用缓存”。 我们需要做的就是告知服务器端“什么条件下不使用缓存”。这个告知方法有两种: 沟通方式就是我们前面提到的Header传递。我们只需在客户端的请求中添加必要的Header值即可。 基于过期时间的验证规则 这个规则比较容易理解,就是如果按照客户端的“标准”,缓存过期了,那就不使用缓存,概括地说就是“超时规则”。 而这个标准就是:客户端给出一个时间点,服务器端的资源缓存超时时间点如果在这个时间点之前,那么缓存就是过期的。 其中,客户端提供的这个时间点,一般使用的是在上次请求时,服务器端返回Header中的expires值。 此时可以使用Key If-Modified-Since,传递的Value就是上面所说客户单要提供的时间点(只能精确秒),验证逻辑见下图说明。 可根据上次请求返回的Header的last-modified值填写不同的时间值进行测试,如命中断点则说明没有使用缓存。 与If-Modified-Since对应的Key还有If-Unmodified-Since。 基于数据ETag的验证规则 在实际应用中有这样一个场景:有一个资源,初次请求后本地缓存了10分钟,第15分钟时缓存已过期,但是服务器端资源并没有变化。 按照上面的验证规则,客户端就会从服务端重新下载资源到本地,进入新的缓存周期。 这就导致了不必要的数据传输,产生了不必要的带宽浪费。这都是我们不希望的结果。而基于ETag的“数据再验证”则可以避免这个问题。 此时我们可以使用Key If-None-Match,传递的Value是一个ETag值。当传递的ETag值与服务器的ETag值不一致,说明资源被变更了,此时将获取最新数据到本地。 如果两个ETag值一致,说明资源并没有发生变更,此时服务器端并不返回资源数据(Response Body将是空数据),状态码也将变为304。 验证逻辑接响应结果见下图说明。可以通过调用Update接口更新资源,然后再通过传递不同的ETag去访问Get接口,来观察缓存是否被使用的规律。 与If-None-Match对应的Key还有If-Match。 到这里,基于HTTP的浏览器缓存机制和使用方法就基本梳理完了。 还剩下一块内容就是在并发场景下的缓存更新问题。不过这个决定留在下篇文章再聊。 努力工作 认真生活 持续学习 以勤补拙 Web性能优化-ReponseCaching 标签:mamicode repo 服务端 信息 ica 中间件 tco routing bsp 原文地址:https://www.cnblogs.com/dinggeonly/p/13374029.html
1 //只有这个包是额外引入的
2 using Marvin.Cache.Headers;
3 using Microsoft.AspNetCore.Mvc;
4 using System.Collections.Generic;
5 using System.Linq;
6
7 namespace ResponseCaching.Controllers
8 {
9 [ApiController]
10 [Route("[controller]")]
11 public class CacheController : ControllerBase
12 {
13 public static IList
Customer对象
1 namespace ResponseCaching
2 {
3 public class Customer
4 {
5 public int Id { get; set; }
6 public string Name { get; set; }
7 public int Age { get; set; }
8 }
9 }
ConfigureServices中添加
services.AddResponseCaching(options=>{});
Configure中添加
app.UseResponseCaching();
ConfigureServices中添加
services.AddHttpCacheHeaders(expirationModelOptionsAction=>{},validationModelOptionsAction=>{});
Configure中添加
app.UseHttpCacheHeaders();
以上两个方法的调用顺序不能颠倒,正确顺序是:
services.AddResponseCaching(options=>{});
services.AddHttpCacheHeaders(expirationModelOptionsAction =>{ }, validationModelOptionsAction =>{ });
……
app.UseResponseCaching();
app.UseHttpCacheHeaders();
1 using Marvin.Cache.Headers;
2 using Microsoft.AspNetCore.Builder;
3 using Microsoft.AspNetCore.Hosting;
4 using Microsoft.Extensions.Configuration;
5 using Microsoft.Extensions.DependencyInjection;
6 using Microsoft.Extensions.Hosting;
7 using System.Linq;
8
9 namespace ResponseCaching
10 {
11 public class Startup
12 {
13 public Startup(IConfiguration configuration)
14 {
15 Configuration = configuration;
16 }
17
18 public IConfiguration Configuration { get; }
19
20 // This method gets called by the runtime. Use this method to add services to the container.
21 public void ConfigureServices(IServiceCollection services)
22 {
23 services.AddControllers();
24
25 services.AddResponseCaching(configureOptions =>
26 {
27 configureOptions.SizeLimit = 50 * 1024 * 1024; //Default:100M
28 configureOptions.MaximumBodySize = 10 * 1024 * 1024;//Default:64M
29 configureOptions.UseCaseSensitivePaths = true; //Default:false
30 });
31
32 //Marvin.Cache.Headers中间件 (只)负责生成Response Header信息
33 services.AddHttpCacheHeaders(expirationModelOptionsAction =>
34 {
35 //Default:60
36 //体现在Hearder中expires和last-modified的时间差
37 expirationModelOptionsAction.MaxAge = 50;
38 //Default:Public
39 expirationModelOptionsAction.CacheLocation = CacheLocation.Public;
40 }
41 , validationModelOptionsAction =>
42 {
43 validationModelOptionsAction.MustRevalidate = true; //Default:false
44
45 //Default:[Accept,Accept-Language,Accept-Encoding]
46 var vary = validationModelOptionsAction.Vary.ToList();
47 vary.AddRange(new string[] { "Id", "Age" }); //留意此细节
48 validationModelOptionsAction.Vary = vary;
49
50 validationModelOptionsAction.VaryByAll = false; //Default:false
51 }
52 );
53 }
54
55 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
56 {
57 if (env.IsDevelopment())
58 {
59 app.UseDeveloperExceptionPage();
60 }
61
62 app.UseResponseCaching();
63 app.UseHttpCacheHeaders();
64
65 app.UseHttpsRedirection();
66
67 app.UseRouting();
68
69 app.UseAuthorization();
70
71 app.UseEndpoints(endpoints =>
72 {
73 endpoints.MapControllers();
74 });
75 }
76 }
77 }
上一篇:HTTP协议详解(真的很经典)
文章标题:Web性能优化-ReponseCaching
文章链接:http://soscw.com/index.php/essay/42926.html