还有就是今天要聊的是,HTTP缓存策略

1.web缓存

缓存的作用就是提升网页加载速度。浏览器加载一个完整的网页势必会引用外部资源(图片,js,css)。若每次加载网页都要去加载这些外部资源则会引起不必要的时间和资源浪费,且会影响用户体验。而解决上述问题需要一个优秀的缓存策略。除此之外,web缓存的优点还有很多,例如:减轻服务器压力

,加快了客户端加载速度,节省网络带宽等。

web缓存按缓存位置,缓存机制可大致分为三类。

  • 1 数据库缓存。
  • 2 服务器缓存。
  • 3 客户端(浏览器)缓存/HTTP缓存。undefined下面着重介绍HTTP缓存。

2.HTTP缓存

HTTP缓存就是将静态资源存储在客户端本地, 下次请求该资源时直接使用本地资源,而不必从服务端加载。

而当服务端的静态资源更新时,本地缓存资源也要更新。当然这需要一系列策略进行约定。

2.1 强缓存策略

所谓强缓存策略即在静态资源有效期内,使用该资源时直接使用本地资源,不请求服务器。

2.1.1 HTTP1.0 expires

在HTTP1.0中,服务器使用expires字段规定过期时间,可以在客户端资源请求的Response Headers 中增加 expires

字段表示资源的过期时间。

它是一个时间戳,当客户端再次请求该资源的时候,会把客户端时间与该时间戳进行对比,如果大于该时间戳则已过期,否则直接使用该缓存资源。

下面用一个实例验证,使用node.js搭建一个web服务,使用定时器创建一个随时间变化的内容。

const http = require("http");

let time = null;

function updateTime() {

  setInterval(() => (time = new Date().toUTCString()), 1000);

}

updateTime();

// time每5秒更新一次

http

  .createServer((req, res) => {

    const { url } = req;

    if ("/" === url) {

      res.end(`

            <html>

                <!-- <meta charset="UTF-8"> -->

                Html Update Time: ${time}

                <script src='main.js'></script>

            </html>

            `);

    } else if (url === "/main.js") {

      const content = `document.writeln('<br>JS Update Time:${time}')`;

      // HTTP1.0 强缓存

      res.setHeader('Expires', new Date(Date.now() + 5 * 1000).toUTCString())

      res.statusCode = 200;

      res.end(content);

    } else if (url === "/favicon.ico") {

      res.end("");

    }

  })

  .listen(3000, () => {

    console.log("服务已启动" + 3000);

  });

上面代码实现的效果是,每次刷新页面,HTML时间一秒钟变化一次,而JS时间由于设置了强缓存因此每5秒变化一次。可以访问http://localhost:3000进行验证。

强缓存验证.jpg

HTML时间和JS时间不一致,说明强缓存生效。

但这种强缓存方式存在一些问题,由于发送请求时是使用客户端时间进行对比,因此一方面是客户端和服务端时间可能不一致,另一方面是客户端的时间(系统时间)是可以自行修改的,因此可能出现服务器资源与本地缓存资源不一致的问题。

2.1.2 HTTP1.1 cache-control

鉴于使用expires字段可能出现的问题,HTTP1.1 新增了 cache-control 字段来解决该问题,所以当 cache-control 和

expires 都存在时, cache-control 优先级更高。 cache-control 主要有 max-age 和

s-maxage、public 和 private、no-cache 和 no-store 等值。 **max-

age字段是一个时间长度,单位秒,表示该资源过了多少秒后失效。当客户端请求资源的时候,发现该资源还在有效时间内则使用该缓存,它不依赖客户端时间。**

.png

下面使用该字段设置强缓存

// HTTP1.0 强缓存

res.setHeader('Expires', new Date(Date.now() + 5 * 1000).toUTCString())

// HTTP1.1 强缓存 cache-control字段   cache-control 优先级更高

res.setHeader('Cache-Control', 'max-age=10')

强缓存验证.jpg

可以看到强缓存生效且cache-control字段优先级比expires高。

鉴于上述强缓存的特点,强缓存的应用场景一般是用于需要定期更新的内容。

2.2 协商缓存

上面所述的强缓存会直接访问本地缓存,没过期的话不会请求服务器,直接使用本地缓存。而协商缓存,顾名思义,

需要和服务器协商一下,看资源是否需要更新。若协商结果是需要更新则会返回更新的内容。若结果是不需要则只返回304状态码,这样可以有效减轻服务器压力。

协商缓存的方式主要有以下两种。

2.2.1 last-modified & if-Modified-Since

该方式的协商过程为:

1 服务器进行静态资源应答时会通过last-modified字段来标示修改时间。

2 浏览器下次请求相同资源会将last-modified时间作为if-modified-since字段的值放在请求报文中用以询问服务器是否该资源过期。

3 服务器需要通过规则判断是否过期。

4 过期时直接返回200并在body中放入更新内容。

5 如果未过期则直接返回304状态码。

下面进行实例验证

 // 协商缓存

 // 方式一 last-modified & if-Modified-Since  通过协商修改时间为基础的策略

 // 首先需要禁用强缓存

 res.setHeader('Cache-Control', 'no-cache') 

 res.setHeader('last-modified', new Date().toUTCString())

 // 设置过期时间为5秒

 if (new Date(req.headers['if-modified-since']).getTime() + 5 * 1000 > Date.now()) {

        console.log('协商缓存命中....')

        res.statusCode = 304

        res.end()

        return

  }

协商缓存验证.jpg

协商缓存命中.jpg

2.2.2 etag & if-None-Match

第二种方式是通过内容判断,一般的做法是将返回内容使用Hash函数进行消息摘要,然后通过对比摘要来判断内容是否需要更新。其协商过程为:

1 服务器进行静态资源应答时通过etag来标示内容摘要。

2 浏览器下次请求相同资源会将etag作为if-none-match字段放在请求报文中用以询问服务器是否该资源过期。

3 服务器需要通过和服务器内容的摘要进行比对确定是否过期。

4 过期时直接返回200并在body中放入更新内容。

5 如果未过期则直接返回304状态码。

下面进行实例验证

    res.setHeader("Cache-Control", "no-cache");

    const crypto = require("crypto"); // nodejs的一个加密模块

    // createHash 创建并返回一个 Hash 对象,该对象可用于生成哈希摘要  digest 字符编码

    const hash = crypto.createHash("sha1").update(content).digest("hex");

    res.setHeader("Etag", hash);

    if (req.headers["if-none-match"] === hash) {

      console.log("Etag协商缓存命中.....");

      res.statusCode = 304;

      res.end();

       return;

    }

Etag协商缓验证.jpg

Etag协商缓命中.jpg

可以看到由于该策略是通过判断内容来决定是否需要更新,因此HTML时间和JS时间会保持一致。因为时间每秒更新一次因此在一秒内刷新页面时会命中协商缓存。因此鉴于协商缓存的特点,其一般用于非定期更新的内容,需要客户端发送请求询问服务器是否需要更新。

正文完