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

缓存:浏览器缓存、DNS缓存和CDN缓存 #19

Open
wqhui opened this issue Dec 10, 2021 · 0 comments
Open

缓存:浏览器缓存、DNS缓存和CDN缓存 #19

wqhui opened this issue Dec 10, 2021 · 0 comments

Comments

@wqhui
Copy link
Owner

wqhui commented Dec 10, 2021

浏览器缓存

浏览器再次发送请求时:

  1. 首先判断强缓存,强缓存生效直接使用强缓存(200 from memory、from disk 先从内存,再从硬盘)
  2. 如果强缓存不生效会判断
    1. 没有设置协商缓存就直接重新请求(200)
    2. 设置了协商缓存则由服务器判断缓存是否失效,没有失效就继续使用缓存(304,同强缓存的获取方式),失效则返回新的文件(200)

缓存分类

强缓存

强缓存主要由响应消息头 Cache-ControlExpires 两个 Header 决定的,在一定时间内不向后端请求,直接使用内存或者磁盘内的缓存内容,前者的优先级更高。

Cache-Control: max-age=31536000 //缓存时间 | no-store:禁止本地缓存 | no-cache:本地缓存,但是强制每次请求直接发送给源服务器,由服务器判断是否使用缓存
Cache-Control: private;max-age=31536000 //默认是public 允许代理
Expires: Wed, 21 Oct 2020 07:28:00 GMT //该时间之后请求过期 Cache-control 优先级比 Expires 优先级高

注意请求头的消息头也可以设置Cache-Control , 表示客户端想要的缓存策略是什么。

一般不经常变动的JS和CSS均会有一个很大的过期时间,搭配上版本号进行缓存策略,减少向后端的请求,如果均不设置只带版本号也可以,因为浏览器自动有缓存的策略。

有些时候,我们会看到Cache-Control: max-age=31536000,那么浏览器帧的会缓存这个数据到一年吗,答案当然是否定的,设置这个值只是因为它是协议允许的最大值,浏览器不会缓存那么久,当缓存满了会优先删除旧的内容,但是CDN会,这样可以减少服务器的压力。

协商缓存

If-none-match/ETagsIf-Modified-Since/Last-Modified 参数决定,通常在强缓存失效后,向服务端通信,服务端确定是否使用缓存,前者优先级更高。

  • ETags 是服务器资源的一个唯一标识,请求响应时消息头会带上这个唯一标识,请求头带上上次请求返回的etag(If-none-match ),由服务器判断资源是否改变。
  • 请求响应时时消息头会带上上次修改时间(Last-Modified),请求时消息头会带上这个修改时间(If-Modified-Since ),由服务器判断资源是否改变。

对于频繁变动的资源,服务器端需设置Cache-Control: no-cache 使浏览器每次都请求服务器,且增加返回请求头 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

版本号

  • revving 技术 : 不频繁更新的文件会使用特定的命名方式:在 URL 后面(通常是文件名后面)会加上版本号。

Service Work

一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。

简单使用

Service Worker是浏览器在后台独立于网页运行的、用JavaScript编写的脚本。
让我们来看看最小的Service Worker长什么样,以及怎么跑起来:

// 不起眼的一行if,除了防止报错之外,也无意间解释了PWA的P:
// 如果浏览器不支持Service Worker,那就当什么都没有发生过
if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
        // 所以Service Worker只是一个挂在navigator对象上的HTML5 API而已
        // scope 参数是可选的,可以用来指定你想让 service worker 控制的内容的子目录。 在这个例子里,我们指定了 '/',表示 根网域下的所有内容。这也是默认值。 
        navigator.serviceWorker.register('/service-worker.js', {scope: './'}).then(function (registration) {
            console.log('我注册成功了');
        }, function (err) {
            console.log('我注册失败了');
        });
    });
}

复制代码以上代码,在load事件触发后,下载并注册了service-worker.js这个文件,Service Worker的逻辑,就写在这里:

// service-worker.js
// 虽然可以在里边为所欲为地写任何js代码,或者也可以什么都不写,
// 都不妨碍这是一个Service Worker,但还是举一个微小的例子:

//监听安装事件,install 事件一般是被用来设置你的浏览器的离线缓存逻辑
this.addEventListener('install', function(event) {
  //通过这个方法可以防止缓存未完成,就关闭serviceWorker
  event.waitUntil(
    //caches api: https://developer.mozilla.org/zh-CN/docs/Web/API/CacheStorage
    caches.open('v1').then(function(cache) {
      //指定要缓存的内容,地址为相对于跟域名的访问路径
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});

//监听fetch,拦截请求
this.addEventListener('fetch', function(event) {
  var response;
  
    event.respondWith(
    caches.match(event.request).then(function(response) {
      if (response) {
        console.log('Found response in cache:', response);

        return response;
      }
      console.log('No response found in cache. About to fetch from network...');

      return fetch(event.request).then(function(response) {
        console.log('Response from network is:', response);
        caches.open('v1').then(function(cache) {
          cache.put(event.request, response);
        });
    		return response.clone();
        
      }).catch(function(error) {
        console.error('Fetching failed:', error);
        throw error;
      });
    })
  );
  
});
应用
  1. 缓存静态资源,Service Worker的一大应用是可以利用CacheStorage API来缓存js、css、字体、图片等静态文件。我们可以在Service Worker的install阶段,指定需要缓存的具体文件,在fetch事件的回调函数中,检查请求的url,如果匹配了已缓存的资源,则不再从服务端获取,以此达到提升网页性能的目的。
  2. 离线体验,将整个页面缓存下来,比如404页面。

详细见:Service Worker 从入门到出门

缓存的位置

缓存主要有4种:Service WorkerMemory CacheDisk CachePush Cache

  • Service Worker
  • Memory Cache,内存中的缓存,主要包含的是当前中页面中已经抓取到的资源(一般是小资源),例如页面上已经下载的样式、脚本、图片等,读取速度快,但是持续时间短,一般页面关闭就被释放。
  • Disk Cache,存储在硬盘中的缓存,能缓存各种资源,读取速度比内存慢,但是能缓存的数据多。
  • Push Cache,推送缓存是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放。

刷新对于缓存的影响

  1. 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
  2. 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
  3. 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。

参考

Service Worker 从入门到出门
浏览器帧的会缓存一年吗

DNS 缓存

有DNS的地方,就有缓存。浏览器、操作系统、Local DNS、根域名服务器,它们都会对DNS结果做一定程度的缓存:

  • 首先查找浏览器自身的缓存
  • 然后是本机的DNS缓存和hosts是否有指定ip
  • 如果还未查到,则查找本地DNS服务器(一般是服务商动态分配的或者我们自己指定的地址)
  • 再查找不到,则向根服务器发送递归查找,边缘DNS -> 顶级DNS (.com/.cn) -> Name Server(由域名提供商维护,我们注册时会提供路径和对应的IP,如果是大型的网站一般还有会有多个IP已支持服务,这会由全局流量管理GTM返回合适的IP)

CDN缓存

在浏览器本地缓存失效后,浏览器会向CDN边缘节点发起请求。类似浏览器缓存,CDN边缘节点也存在着一套缓存机制。CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control判断资源是否过期
其优势是:

  1. CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。
  2. 大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源服务器的负载。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant