百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

PHP开发,如何利用HTTP缓存来加快你的网站速度

wxin55 2024-10-26 16:38 9 浏览 0 评论

缓存在web环境各个环节都有实现,有CPU缓存、文件缓存、程序的Opcode缓存(APC,eAccelerator)、内存缓存(Memcached,Redis)、代理服务器(Nginx,Squid)、数据库的查询缓存、基于HTTP的客户端缓存。其中HTTP缓存是离用户最近的缓存,访问最快,合理使用可以加快数据加载速度、减少服务器的开销。

HTTP缓存通过设置一些头加以控制,有一部分是控制要不要缓存、怎么缓存以及缓存多久的,还有一部分是决定缓存过期以后怎么处理的。下面只列出最主要的:

  • 缓存控制
  • Cache-Control
  • public:公共,可以被客户端和代理服务器(如Nginx、Squid)缓存
  • private:私有,只能被当前客户端缓存
  • no-store:不缓存,客户端不维护缓存
  • no-cache:并不是不缓存,但是每次都会请求服务器,可以配合ETag和Last-Modified来避免响应重复的内容,适用于需要显示最新内容的场景
  • max-stale:客户端可以设置它来使用已经过期的缓存,单位为秒,这个时间是已经过期了多少秒的意思
  • must-revalidate:因为客户端可以设置max-stale来使用已经过期的缓存,服务器可以设置它来强制客户端不允许使用过期的缓存,必须重新请求服务器
  • Pragma:相比Cache-Control,它是不支持响应头的,通常是为了向后兼容HTTP/1.0
  • 缓存时间
  • max-age:距离请求发起的时间的秒数,Cache-Control指令之一
  • Expires:过期时间(GMT格式),和max-age类似,但是Expires受客户端时间影响,是HTTP/1.0的标准,max-age是它的改良版,优先级为:max-age -> Expires
  • 缓存校验(缓存过期后通过比较来检查是否继续使用)
  • Last-Modified:服务端在响应头里面设置此项来告知客户端资源的修改时间(GMT格式),客户端会在下次请求时自动加上If-Modified-Since: ,服务端以此来比对缓存是否有更新
  • ETag(Entity Tags):依赖Last-Modified来检查缓存有缺陷,比如文件的修改时间变了但是内容没有变,又比如它只能精确到秒,而ETag是比对内容,可以理解为md5值,服务端响应ETag后客户端会在下次请求时自动带上If-None-Match: 。ETag比Last-Modified开销大,如果可以用Last-Modified尽量用Last-Modified

注意private的应用场景,比如个人中心的url是/userinfo.php,所有人的url都是相同的,这个时候如果用了public走了代理缓存,会导致所有人共享一个缓存,所以这种时候需要使用private。

实例

缓存一分钟,一分钟内直接读取本地缓存,一分钟后重新请求服务器:

Cache-Control: public, max-age=60, must-revalidate

上面这种方式一旦缓存过期就会重新请求服务器返回最新的内容,如果内容并没有变化,那不是白传了?有没有办法在缓存过期以后判断一下如果内容没有改变则继续用本地的缓存呢?很简单!ETag可以帮到你,有效期内直接读取本地缓存,过期后跟服务器比对ETag,相同则服务器会返回304表示缓存还可以继续用,而不返回实际内容,节约了时间和带宽。

缓存一分钟,一分钟内直接读取本地缓存,一分钟以后跟服务器比对ETag,如果ETag没有变化,那么接下来的一分钟内还是直接读取本地缓存:

Cache-Control: public, max-age=60, must-revalidate
ETag: abc

每次都要跟服务器比对ETag是否相同,适合更新稍微比较频繁并且需要及时显示最新内容的资源:

Cache-Control: public, no-cache
ETag: abc

不缓存:

Cache-Control: no-store

一图胜千言

结合以上知识点,以服务端的视觉,结合需求来看具体需要怎么设置,画个流程图:

动态脚本的缓存

我们一般会在Nginx上配置静态文件的缓存,而常常忽略了动态页面和API的缓存,其实它们也是可以设置缓存的,在代码里面实现更加灵活,以PHP为例,新建一个文件命名为cache.php:

header('Cache-Control: public, max-age=60, must-revalidate');
$time = date('H:i:s');
$data = 666;
$etag = md5($data);
if($_SERVER['HTTP_IF_NONE_MATCH'] == $etag){
 header("HTTP/1.1 304 Not Modified");
 header('ETag: '.$etag);
 exit;
}else{
 header('ETag: '.$etag, true, 200);
}
echo $time.'|'.$data;

上面的代码设置缓存一分钟,过期之后客户端需要和服务端通过比对ETag来确认缓存是否可以继续使用。现在可以一直点击刷新按钮看看效果,奇怪,怎么不是直接读本地缓存,老是请求到服务器返回304了呢?

浏览器的刷新策略

点击浏览器的刷新按钮或者F5,请求头会加上Cache-Control: max-age=0,Ctrl + F5强刷会在请求头加上Cache-Control: no-cache,所以这两个操作都会导致浏览器放弃读取本地缓存而直接请求服务器。点击链接跳转和后退或者前进按钮是不会加上这些头的。

如果想看200 from disk cache读取本地缓存的效果,我们需要一个页面来做个跳转:

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="utf-8">
</head>
<body>
 <p><a href="./cache.php">这个页面被缓存了,打开看看吧。</a></p>
</body>
</html>

ajax缓存

如果是ajax请求怎么实现缓存呢?也是一样的,JS只要控制好ETag和Last-Modified就好,jquery的ajax方法里有个ifModified可以做到自动处理。

 $.ajax({
 type: 'GET',
 url: 'api.php',
 cache: true,
 ifModified: true,
 success: function(data, status, xhr){
 if(data) {
 console.log(data);
 }
 } 
 });
});
</script>
</body>
</html>

如果希望每次都读取最新的内容,如果内容没更新就读缓存,可以这样做:

header('Cache-Control: no-cache');
$data = 'abcdef';
$etag = md5($data);
if($_SERVER['HTTP_IF_NONE_MATCH'] == $etag) {
 header("HTTP/1.1 304 Not Modified");
 header('ETag: '.$etag);
 exit;
}
header('ETag: '.$etag);
echo $data;

相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: