articles's People
articles's Issues
前端业务使用的缓存
为什么需要缓存
- 减少延迟
- 减少网络带宽消耗
- 减少服务器负载
怎么使用缓存
- 客户端缓存
- 服务端缓存
客户端缓存
Application Cache
什么是应用程序缓存(Application Cache)?
HTML5 引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问。
三个优势
- 离线浏览 - 用户可在应用离线时使用它们
- 速度 - 已缓存资源加载得更快
- 减少服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源。
注意事项
- 浏览器对缓存数据的容量限制可能不太一样(某些浏览器设置的限制是每个站点 5MB)。
- 如果manifest文件,或者内部列举的某一个文件不能正常下载,整个更新过程都将失败,浏览器继续全部使用老的缓存。
- 引用manifest的html必须与manifest文件同源,在同一个域下。
- FALLBACK中的资源必须和manifest文件同源。
- 当一个资源被缓存后,该浏览器直接请求这个绝对路径也会访问缓存中的资源。
- 站点中的其他页面即使没有设置manifest属性,请求的资源如果在缓存中也从缓存中访问。
- 当manifest文件发生改变时,资源请求本身也会触发更新。
- 系统会自动缓存引用清单文件的 HTML 文件
- manifest文件中CACHE则与NETWORK,FALLBACK的位置顺序没有关系,如果是隐式声明需要在最前面
个人观点
处理不妥当会导致应用混乱,而且W3C协议已经去除掉该协议了,如果项目没有非必要不建议采用
PWA缓存
Progressive Web App, 简称 PWA,是提升 Web App 的体验的一种新方法,能给用户原生应用的体验。
主要特点
- 可靠 - 即使在不稳定的网络环境下,也能瞬间加载并展现
- 体验 - 快速响应,并且有平滑的动画响应用户的操作
- 粘性 - 像设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面
国内支持
[https://lavas.baidu.com/ready/browser?lang=zh]
[https://caniuse.com/#feat=web-app-manifest]
[https://caniuse.com/#search=Cache]
[https://plus.ucweb.com/docs/spm=ucplus.11199946.c-header.6.44cad318HwYtVd]
个人观点
- PWA对比AppCache而言网络请求有sw作为中间件较为可控
- 建议采用谷歌提供的工具进行开发会更加得心应手,例如:网络请求优先,缓存优先,只读取缓存,新旧缓存更替删除等功能
- 可以实现离线支持访问的功能
- 现阶段浏览器的支持度还不高
localStorage,sessionStorage,memory,cookie
- localStorage: 没有时间限制的数据存储
- sessionStorage: 页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话
- memory: JavaScript变量
- cookie: 每次请求都会带上对应的值
个人观点
- localStorage 可以做长期或中短期缓存.
- 例如:每次存入数据前都设置expire值,用来标明过期时间,下次读取的时先判断expire是否超时,再读取内容
- sessionStorage 可以做用户当次访问站点缓存,关闭站点后缓存清空.
- 例如:用户一次性的登录状态,一些临时数据
- memory 没有触发页面刷新可以一直使用的缓存
- 例如:当前页面的临时数据刷新或者下次访问均要重新获取的数据
- cookie 作为全局变量使用
- 例如: 服务器session功能,自动登录,跟踪用户行为
HTTP缓存
强制缓存
- HTTP 1.0
- Expires 浏览器资源缓存过期时间
Wed, 30 May 2018 21:01:11 GMT
如果客户端上的时间跟服务器上的时间不一致,那缓存时间可能就没啥意义了
- Pragma Pragma的优先级是高于Cache-Control
- no-cache
- Expires 浏览器资源缓存过期时间
- HTTP 1.1
- Cache-Control 缓存控制
- no-cache 使用对比缓存来验证缓存数据
- no-store 所有内容都不会缓存,强制缓存,对比缓存都不会触发
- max-age=xxx 缓存的内容将在 xxx 秒后失效
- Cache-Control 缓存控制
Pragma -> Cache-Control -> Expires
对比缓存
-
Last-Modified
服务器在响应请求时,告诉浏览器资源的最后修改时间。
-
If-Modified-Since
再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间
- 服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。
- 若资源的最后修改时间大于If-Modified-Since,说明资源又被改动过,则响应整片资源内容,返回状态码200;
- 若资源的最后修改时间小于或等于If-Modified-Since,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache
-
Etag
服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识
-
If-None-Match
再次请求服务器时,通过此字段通知服务器客户段缓存数据的唯一标识
- 服务器收到请求后发现有头If-None-Match 则与被请求资源的唯一标识进行比对
- 不同,说明资源又被改动过,则响应整片资源内容,返回状态码200
- 相同,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache
个人观点
能用强制缓存就用�强制缓存,减少带宽和服务器压力,如果更新了资源换个文件名就好啦
服务端缓存
Nginx缓存
Nginx对客户已经访问过的内容在Nginx服务器本地建立副本,这样在一段时间内再次访问该数据,就不需要通过Nginx服务器再次向后端服务器发出请求,所以能够减少Nginx服务器与后端服务器之间的网络流量,减轻网络拥塞,同时还能减小数据传输延迟,提高用户访问速度。同时,当后端服务器宕机时,Nginx服务器上的副本资源还能够回应相关的用户请求,这样能够提高后端服务器的鲁棒性。
配置
- proxy_cache_path 有两个必填参数,第一个参数为 缓存目录,第二个参数
- keys_zone keys_zone 指定缓存名称和占用内存空间的大小
- proxy_cache keys_zone的缓存名称 启用proxy cache,并指定key_zone。另外,如果proxy_cache off表示关闭掉缓存。nginx默认会缓存所有 get 和 head 方法的请求结果,缓存的key默认使用请求字符串
- proxy_cache_key "$host$request_uri$cookie_user" 自定义key
- proxy_cache_min_uses 指定请求至少被发送了多少次以上时才缓存,可以防止低频请求被缓存
- proxy_cache_methods 指定哪些方法的请求被缓存 GET HEAD POST
- proxy_cache_valid 响应状态码为200 302时,10分钟有效 200 302 10m
- proxy_no_cache 不生成cache文件
- proxy_cache_bypass 绕开缓存, 如果任何一个参数值不为空,或者不等于0,nginx就不会查找缓存,直接进行代理转发 $cookie_nocache
- proxy_cache_use_stale 当作为cache的NGINX收到源站返回error、timeout或者其他指定的5XX错误,并且在其缓存中有请求文件的陈旧版本,则会将这些陈旧版本的文件而不是错误信息发送给客户端 timeout http_500 http_502 http_503 http_504
upstream_cache_status
- HIT —— 响应包含来自缓存的最新有效的内容
- EXPIRED —— 缓存中的某一项过期了,来自原始服务器的响应包含最新的内容
- MISS —— 响应在缓存中找不到,所以需要在服务器中取得。这个响应之后可能会被缓存起来
- BYPASS —— 响应来自原始服务器而不是缓存,因为请求匹配了一个proxy_cache_bypass,这个响应之后可能会被缓存起来
- UPDATING —— 内容过期了,因为相对于之前的请求,响应的入口(entry)已经更新,并且proxy_cache_use_stale的updating已被设置
- REVALIDATED —— proxy_cache_revalidate命令被启用,NGINX检测得知当前的缓存内容依然有效(If-Modified-Since或者If-None-Match)
个人观点
- 如果后端渲染页面需要消耗服务器资源较多的话,考虑针对页面做定时缓存
- 例如: 请求一个页面需要请求十个�接口并且通过各种计算才能渲染出页面,这时候可以针对该页面做缓存10s,这样子10s内的用户请求就到后端服务器,而是在代理服务器读取缓存即可
- 对静态资源缓存,这样子只要有一个用户访问过该资源,后续所有用户均读取缓存即可
Node缓存
共享内存服务 Redis & Memcached
- Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
- 内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
- 性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
- [https://yq.aliyun.com/articles/516432?spm=5176.11065265.1996646101.searchclickresult.12e07b20YqOvtX]
- [https://yq.aliyun.com/articles/79886?spm=5176.11065265.1996646101.searchclickresult.12e07b20YqOvtX]
个人观点
- redis和memcached对于一般业务使用区别不大,如果需要数据结构多样性用redis,结构简单的用memcached或redis都可以
-对可以缓存的接口尽量采用缓存,减少io操作,qps可以提升 - 千人一面的页面也应该采用缓存
服务器内存 Memory
- 限制内存当缓存,要限制好大小,做好释放
- 进程之间不能共享内存,所以用内存做缓存也是
- nodejs在执行JavaScript时,内存受到v8限制,64位约为1.4g,32位0.7g
垃圾回收时,js线程会暂停执行,大量的堆内存回收严重影响性能
个人观点
-服务器内存对比共享内存而言,性能更高更好更快,因为少了网络请求io操作,但是如果处理不好会出现问题,针对响应时间和处理要求的业务可以考虑使用
-例如:抢购功能
PWA 相关设置 --- manifest
Manifest
引入manifest文件
<link rel="manifest" href="/manifest.json">
Manifest Property
-
name
:"name": "Google I/O 2017"
- 为应用程序提供一个人类可读的名称,例如在其他应用程序的列表中或作为图标的标签显示给用户。
-
short_name
:"short_name": "I/O 2017"
- 为应用程序提供简短易读的名称。这是为了在没有足够空间显示Web应用程序的全名时使用。
-
theme_color
:"theme_color": "aliceblue"
- 定义应用程序的默认主题颜色。这有时会影响操作系统显示应用程序的方式(例如,在Android的任务切换器上,主题颜色围绕着应用程序)。
-
background_color
:"background_color": "red"
- 定义Web应用程序的预期背景颜色。此值重复应用程序样式表中已有的内容,但在样式表加载之前清单可用时,浏览器可以使用此值来绘制Web应用程序的背景颜色。这将启动Web应用程序和加载应用程序内容之间的平滑过渡。
-
start_url
:"start_url": "./?utm_source=web_app_manifest"
- 指定用户从设备启动应用程序时加载的URL。如果以相对URL的形式给出,则基本URL将是清单的URL。
-
orientation
:"orientation": "portrait-primary"
- 应用程序的顶级浏览上下文的默认方向。
- any
- natural
- landscape
- landscape-primary
- landscape-secondary
- portrait
- portrait-primary
- portrait-secondary
-
display
:"display": "standalone"
- 显示模式
- fullscreen 所有可用的显示区域都被使用,并且没有显示用户代理chrome。
- standalone 该应用程序将看起来像一个独立的应用程序。这可以包括具有不同窗口的应用程序,在应用程序启动器中的它自己的图标等。在这种模式下,用户代理将排除用于控制导航的UI元素,但是可以包括其他UI元素,例如状态栏。
- minimal-ui 该应用程序将看起来像一个独立的应用程序,但将有一组用于控制导航的UI元素。元素将因浏览器而异。
- browser 该应用程序在传统的浏览器标签或新窗口中打开,具体取决于浏览器和平台。这是默认的。
-
dir
"dir": "rtl", "lang": "ar", "short_name": "أنا من التطبيق!"
- 设置name,short_name和description文字的方向
- ltr 左到右
- rtl 右到左
- auto 默认
-
description
:"description": "The app that helps you find the best food in town!"
- 项目描述
-
scope
:"scope": "/myapp/"
- 定义此Web应用程序的应用程序上下文的导航范围。这基本上限制了应用清单时可以查看的网页。如果用户在范围之外浏览应用程序,则返回到正常的网页。
- 如果范围是相对URL,则基本URL将是清单的URL。
-
lang
:"lang": "en-US"
- 指定name和short_name成员中的值的主要语言。该值是包含单个语言标记的字符串。
-
icons
"icons": [ { "src": "icon/lowres.webp", "sizes": "48x48", "type": "image/webp" }, { "src": "icon/lowres", "sizes": "48x48" }, { "src": "icon/hd_hi.ico", "sizes": "72x72 96x96 128x128 256x256" }, { "src": "icon/hd_hi.svg", "sizes": "72x72" } ]
- 指定可在各种上下文中用作应用程序图标的图像对象数组。例如,它们可用于在其他应用程序列表中表示Web应用程序,或将Web应用程序与OS的任务切换器和/或系统首选项集成 。
- sizes 包含空格分隔的图像尺寸的字符串。
- src 图像文件的路径。如果src是相对URL,则基本URL将是清单的URL。
- type 提示图像的媒体类型。此成员的目的是允许用户代理快速忽略不支持的媒体类型的图像。
-
prefer_related_applications
:"prefer_related_applications": false
- 指定一个布尔值,提示用户代理向用户指示指定的相关应用程序(请参见下文)可用,并建议通过Web应用程序。只有当相关的本地应用程序确实提供了一些Web应用程序无法做到的事情时,才应该使用它。
-
related_applications
:"related_applications": [ { "platform": "play", "url": "https://play.google.com/store/apps/details?id=com.example.app1", "id": "com.example.app1" }, { "platform": "itunes", "url": "https://itunes.apple.com/app/example-app1/id123456789" }]
- platform 可以找到应用程序的平台。
- url 可以找到应用程序的URL。
- id 用于表示指定平台上的应用程序的ID
Manifest Example
{
"name": "HackerWeb",
"short_name": "HackerWeb",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"description": "A simply readable Hacker News app.",
"icons": [{
"src": "images/touch/homescreen48.png",
"sizes": "48x48",
"type": "image/png"
}, {
"src": "images/touch/homescreen72.png",
"sizes": "72x72",
"type": "image/png"
}, {
"src": "images/touch/homescreen96.png",
"sizes": "96x96",
"type": "image/png"
}, {
"src": "images/touch/homescreen144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "images/touch/homescreen168.png",
"sizes": "168x168",
"type": "image/png"
}, {
"src": "images/touch/homescreen192.png",
"sizes": "192x192",
"type": "image/png"
}],
"related_applications": [{
"platform": "web"
}, {
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=cheeaun.hackerweb"
}]
}
PWA 相关设置 --- serviceWorker
ServiceWorker
注册worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {
// registration worked
console.log('Registration succeeded. Scope is ' + reg.scope);
}).catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
}
安装和激活
this.addEventListener('install', function(event) {
event.waitUntil(
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/',
'/sw-test/gallery/bountyHunters.jpg',
'/sw-test/gallery/myLittleVader.jpg',
'/sw-test/gallery/snowTroopers.jpg'
]);
})
);
});
删除旧缓存
this.addEventListener('activate', function(event) {
var cacheWhitelist = ['v2'];
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (cacheWhitelist.indexOf(key) === -1) {
return caches.delete(key);
}
}));
})
);
});
请求拦截
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).catch(function() {
return fetch(event.request).then(function(response) {
return caches.open('v1').then(function(cache) {
cache.put(event.request, response.clone());
return response;
});
});
}).catch(function() {
return caches.match('/sw-test/gallery/myLittleVader.jpg');
})
);
});
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.