企业级 PHP 高并发解决方案 0x03 浏览器缓存和压缩优化技术
HTTP 缓存机制
缓存分类
在HTTP缓存模型中,如果请求成功会有三种情况。
- 200 from cache
- 304 Not Modified
- 200 OK
200 from cache: 直接从本地缓存中获取响应,最快速,最省流量,因为根本没有向服务器发送请求I看到。
浏览器本身就有缓存机制,当我们浏览器检测到该资源在本地存在,那么就不需要向服务器发送请求,从下图中我们可以看到size没有大小显示的是 from cache,就是读取的缓存。
查看其响应头我们可以看到下图。
304 Not Modified: 协商缓存,浏览器在本地没有命中的情况下请求头中发送一定的教研数据到服务端,如果服务端数据没有改变,浏览器从本地缓存响应,返回304。
快速,发送的数据很少,只会返回一些基本的响应头信息,数据量很小,不发送实际响应体。
200 OK: 以上两种缓存全部失效,服务器返回完整响应。没有用到缓存,相对较慢,对于200 OK不能称之为缓存,因为根本没有用到缓存机制。
相关 Header
- Pragma
- Expires
- Cache-Control
Pragma: HTTP 1.0 时代的遗留产物,该字段被设置为no-cache时,会告知浏览器禁用本地缓存,即每次都向服务器发送请求。
Expires:HTTP 1.0 时代用来启用本地缓存的字段,expires值对应一个形式如Thu, 31 Dec 2037 23:55:55 GMT的格林威治时间,告诉浏览器缓存实现的时刻,如果还没到该时刻,标明缓存有效,无需发送请求。
浏览器与服务器的时间无法保持一致,如果时间差比较大,就会影响缓存结果。我们可以保证服务器的时间,但是无法保证客户端的时间与服务器时间的一致。(QQ 空间就有对客户端时间进行检测,感兴趣的可以将本地时间调整一下然后去访问QQ空间)
Cache-Control:HTTP 1.1 针对 Expires 时间不一致的解决方案,运用 Cache-Control 告知浏览器缓存过期时间间隔,而不是时刻,即使具体时间不一致,也不影响缓存的管理。
在Cache-Control下我们还可以设置一些头信息:
- no-store:禁止浏览器缓存响应
- no-cache:不允许直接使用本地缓存,先发起请求和服务器协商,也就是协商缓存
- max-age=delta-seconds:告知浏览器该响应本地缓存有效的最长期限,以秒为单位。
优先级: Pragma > Cache-Control > Expires
在上图中,我们可以看到上面的头信息,设置了Cache-Control和Expires。
协商缓存当浏览器没有命中本地缓存,如果本地缓存过期或者响应中生命不允许直接使用本地缓存(Pragma或者Cache-Control设置成了no-cache),那么浏览器肯定会发起服务端请求。服务端会验证数据是否修改,如果没有通知浏览器使用本地缓存。
相关的Header:
- Last-Modified:通知浏览器资源的最后修改时间,格式如上图。
- If-Modified-Since:这是与Last-Modified相对应的一个头信息,得到资源的最后修改时间后,会将这个信息通过If-Modified-Since提交到服务器进行检查,如果没有修改则返回304状态码。
- ETag: HTTP 1.1 推出,文件的指纹标识符,如果文件内容修改,指纹会改变,Last-Modified只能精确到秒,而 ETag 如果发生了改变,它就会改变,它就相当于文件的标识("78437822c-6739"),也更加准确。
- If-Node-Match:与ETag相对应的一个头信息,本地缓存失效,会携带此值去请求服务端,服务端判断该资源是否改变,如果没有改变直接使用本地缓存,返回304
通过下面的两张图,我们可以看到完整的协商缓存交互:
缓存策略的选择
适合的内容:
- 不变的图像,如logo,图标等
- js、css静态文件
- 可下载的内容,媒体文件
建议使用协商缓存:
- HTML文件
- 经常替换的图片
- 经常修改的js、css
- js、css问价你的加载可以加入文件的签名来拒绝缓存 (index.css?签名、index.签名.js)
不建议缓存的内容:
- 用户隐私等敏感数据
- 经常改变的 api 数据接口
NGINX 配置缓存策略
首先,我们使用PHP模拟NGINX的缓存。
<?php
//1. 获取If-Modified-Since
$since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
//2. 设置过期时间一个小时
$lifetime = 3600;
//3. 如果没有过期直接返回304告诉浏览器使用本地缓存
if (strtotime($since) + $lifetime > time()) {
header('HTTP/1.1 304 Not Modified');
exit;
}
//4. 设置最后修改时间
header('Last-Modified:'. gmdate('D, d M Y H:i:s', time()). ' GMT');
//5. 网页显示的内容,用于验证是否开启缓存
echo time();
上述代码就是NGINX的缓存原理,不过NGINX判断的不是3600秒,而是文件的修改时间。
本地缓存配置
add_header指令:添加状态码为2XX和3XX的响应头信息。
add_header name value [always];
可以设置Pragma/Expires/Cache-Control,可以继承
expires指令:通知浏览器过期市场
expires time;
- 为负值时表示Cache-Control:no-cache;
- 为正或0时,就表示Cache-Control:max-age=指定的时间;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}
location ~ .*\.(js|css)?$
{
expires 12h;
}
当设为max时,会把Expires设置为"Thu, 31 Dec 2037 23:55:55 GMT",Cache-Control设置到10年。
协商缓存配置
ETag指令:指定签名
etag on | off 默认是on
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
etag on;
}
Cache-Control
location ~ .*\.(js|css)?$
{
add_header cache-control max-age = 3600;
}
前端代码和资源的压缩
优势
- 让资源文件更小
- 加快文件在网络中的传输
- 让网页更快的展示
- 降低带宽和流量的开销
压缩方式
- JS、CSS、图片、HTML代码的压缩
- Gzip压缩
JavaScript代码压缩
JavaScript压缩的原理一般是去掉多余的空格和回车、替换长变量名、简化一些代码的写法。
压缩工具有很多,有在线工具、有应用程序、有编辑器插件。
常用的压缩工具:
- UglifyJs: 压缩、语法检查、美化代码、代码缩减、转化
- YUI compressor:来自Yahoo,只有压缩功能
- Closure Compiler:来自Google、功能和UglifyJs有些类似,压缩的方式不太一样。
CSS代码压缩
原理和JavaScript压缩原理类似,同样是去除空白符、注释并且优化一些CSS语义规则。
常用的压缩工具:
- YUI compressor:来自Yahoo,只有压缩功能
- CSS Compiler:压缩时候可以选择模式,也可以对一些语法进行优化
HTML代码压缩
不建议使用代码压缩,有时会破坏代码结构,可以使用Gzip压缩,当然也可以使用htmlcompressor工具,不过转换后一定要检查代码结构。
图片压缩
除了代码的压缩外,有时对图片的压缩也是很有必要的,一般情况下图片在Web系统的比重都比较大。
压缩工具
- tinypng
- JpegMini
- ImageOptim
Gzip压缩
GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNⅨ系统的文件压缩。我们在Linux中经常会用到后缀为.gz的文件,它们就是GZIP格式的。现今已经成为Internet 上使用非常普遍的一种数据压缩格式,或者说一种文件格式。
HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的。
NGINX配置:
gzip on|of #是否开启gzip
gzip_buffres 32 4k| 16 8k; #缓冲(在内存中缓冲几块,每块多大)
gzip_comp_level [1-9] #推荐6 压缩级别(级别越高,压缩的越小,越浪费CPU计算资源)
gzip_disable #正则匹配UA什么样的Uri不进行gzip
gzip_min_length 200 #开始压缩的最小长度
gzip_http_version 1.0|1.1 #开启压缩的http协议版本
gzip_proxied #设置请求者代理服务器,该如何缓存内容
gzip_types text/plain applecation/xml #对那些文件类型使用压缩
gzip_vary on | off #是否传输gzip压缩标志
其他工具
- 自动化构建工具Grunt