Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

web 性能优化 - HTTP 缓存 #39

Open
hjzheng opened this issue Feb 21, 2017 · 0 comments
Open

web 性能优化 - HTTP 缓存 #39

hjzheng opened this issue Feb 21, 2017 · 0 comments
Labels

Comments

@hjzheng
Copy link
Member

hjzheng commented Feb 21, 2017

随着互联网的发展,前端页面变的越来越复杂,越来越多的事情,被拿到前端来处理。这就导致 HTML 页面会被加载大量的资源 包括 JS,CSS 以及图片等。

本文从 HTTP 缓存的角度,来谈谈如何优化页面资源加载性能。

HTTP协议

在谈 HTTP 缓存之前,让我们先来聊聊 HTTP 协议,HTTP 作为一个非常重要的协议,它的发展却驻足不前,翻开的它的历史,它竟然只有三个版本:

  • HTTP/0.9,在HTTP 1.0 之前,HTTP 标准并未正式建立,所以统称 HTTP/0.9。
  • HTTP/1.0, 1996年正式公布的版本,被记载在 RFC1945。
  • HTTP/1.1, 1997年1月公布的,是目前的主要版本,它也是我们将要讨论的主要版本。

然而 HTTP 协议只是 TCP/IP 协议族的一小部分,为了了解客户端和服务器是如何通信的,就必须了解 TCP/IP 协议族.

TCP/IP 协议族进行了分层,从上到下依次是,应用层,传输层,网络层,数据链路层。 HTTP 协议属于应用层,TCP 协议属于传输层。

HTTP 协议是建立在 TCP 协议基础上,让我们来简单描述一下,浏览器与服务器如何使用 HTTP 协议进行通信的:

  • 浏览器解析出 url 中的主机名
  • 浏览器查询这个主机名的 IP 地址 (233.44.78.5)(DNS 服务)
  • 浏览器获得端口号(80)
  • 浏览器发起到 233.44.78.5 端口为 80 的连接(三次握手后,建立起一条从浏览器到服务器的 TCP 连接)
  • 连接建立后,浏览器发送一条 HTTP GET 请求报文
  • 浏览器从服务器获得 HTTP 响应报文
  • 浏览器关闭连接

浏览器和服务器通信发送的数据块是 HTTP 报文。HTTP 报文由起始行,首部和主体三部分组成。
HTTP 报文一般分为请求报文和响应报文。请求报文的起始行由方法,URI 和 HTTP 版本组成。响应报文的起始行由状态码,原因短语组成。它们的首部都有首部字段和值组成。主体是需要发送和返回的数据。首部和主体之间用 CRLF 空行分割。

配置响应首部字段

如何利用好 HTTP 缓存的关键在于响应和请求的首部字段。

因为每个浏览器都实现了 HTTP 缓存,所以无需设置请求的首部字段,目前所要做的就是,确保每个服务器响应都提供正确的 HTTP 首部,以指导浏览器何时可以缓存资源以及可以缓存多久。

使用 ETag 验证缓存的资源是否被修改

ETag 为我们提供一种验证资源是否被修改的方法,举例,浏览器首次请求资源,服务器返回一个带 ETag 首部的响应,大约过了一小时后,浏览器再次发起同样的请求,此时浏览器自动将 ETag 的值作为 If-None-Match 首部的值,发送给服务器,当服务器使用该值,检查响应的资源是否被修改,如果未修改,则返回 304 Not Modified 响应。注意这里我们并未下载资源,节约时间和宽带资源。

使用 Cache-Control 指导浏览器如何缓存资源

Cache-Control 可以设置的值有: no-cache 或 no-store, public 或 private, max-age。

no-cache 或 no-store
no-cache 表示必须先与服务器确认返回的资源是否被修改,然后才能确认是否使用缓存资源来满足后续相同的请求。因此,如果存在 ETag,no-cache 会发起往返通信来验证缓存的资源,如果资源未被更改,可以避免下载。

no-store 禁止浏览器和所有中继缓存(例如代理缓存服务器)存储返回的响应资源。

public 或 private

public 表示允许浏览器和任何中继缓存缓存响应资源
private 只允许浏览器缓存响应资源

max-age

max-age 指定从当前请求开始,允许获取的响应资源被重用的最长时间 例如:max-age=120 表示响应可以再缓存和重用 120 秒

配置 Cache-Control 的策略
如下图
image

我们可以根据自己实际的业务和不同的资源制定不同的缓存策略:

  • 针对 HTML 页面,使用 no-cache, 例如 index.html Cache-Control: no-cache
  • 针对 JS 和 CSS 文件,可以进行长时间缓存, 如果 JS 中有一些敏感信息,可以使用 private 禁止中继缓存存储。
    • 例如 main.2da43e32.css Cache-Control: max-age=31536000
    • 例如 main.1a3dq2q6.js Cache-Control: private max-age=31536000
  • 针对 图片或 字体文件,可以设置一个月(31天),例如 logo.png Cache-Control: public max-age=2678400

总结

在定义缓存策略时, 有一些准则是可以参考的:

  1. 服务器需要提供 ETag。
  2. 明确中继缓存可以缓存哪些资源。
  3. 根据资源的特点设置缓存时间。
  4. 针对同一资源,将变动的资源和不变资源区分。例如 JS 文件在压缩合并时,将 lib(一些库和框架js) 和 app(业务js)分离。设置不同的缓存策略。

配置 nginx

nginx 服务器中配置 ETag 和 Cache-Control

server {
  listen 80;
  server_name get-set.cn;
  root /data/hjzheng/build/;
  charset utf-8;
  
  etag on; # 启用 ETag
  
  try_files $uri =404;
  
  location ~* (.+)\.html {
     add_header Cache-Control no-cache;
  }

  location ~* (.+)\.js {
     add_header Cache-Control private;
     expires 1y; # 设置 max-age
  }
  
  location ~* (.+)\.css {
     add_header Cache-Control public;
     expires 1y; 
  }
  
  location ~* (.+)\.(jpg|png|gif|eot|svg|ttf|woff) {
     add_header Cache-Control public;
     expires 31d;
  }
}

其他

注意, 本文主要讲述 Cache-Control 中值在 response 中设置的作用,在 Request 中含义略有不同:
image

资料

常见服务器配置
nginx配置location总结及rewrite规则写法

@hjzheng hjzheng added the 分享 label Feb 21, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant