全站缓存时代

原则:动静分离,分级缓存,主动失效。html

Web 开发中,接口会被分为如下几类:前端

  1. 纯静态页面。打死我都不会修改的页面。很长一段时间内,基本上不会修改。好比:关于咱们。git

  2. 纯动态页面。实时性,个性化要求比较高。页面变化很大,或者每一个用户看到的都不同,好比:朋友圈。github

  3. 短时静态页面。在必定时间内基本不会变化,或者是容忍不须要实时更新。好比:文章、新闻。redis

  4. 动静结合页面。这个页面既有动态,也有静态内容。也是实际应用中最多的。后端

对于以上类型的页面,能够作不一样的缓存方案。各位大神们应该根据本身业务的状况,灵活调整缓存方案。如下内容能够做为参考。api

模板渲染

高速发展的模板引擎,给前端渲染带来了活力。Mustache、jade、hbs 灵活的模板语法让页面开发变得更省力和高效。浏览器

HtmlDOM == VeiwEngine.render(template ,data);缓存

浏览器只认识 DOM 结构的字符串,也就是常说的 HTML5 格式。对于前端来渲染 DOM,仍是后端渲染的问题,在此不用讨论,为了状况前端的性能和体验,后端渲染会更合适。对于同一个页面,每次请求都会产生一次渲染吗?渲染老是要计算的,这样多浪费服务器性能啊!确实是这样,除非你用了缓存。服务器

页面缓存的方案

1. 纯静态页面

直接放 CDN。纯静态页面的访问量通常不会很大,程序直接响应也是能够的。

2. 纯动态页面

都说是动态页面了,那就不要作页面缓存了。能够考虑作数据缓存,或者是 redis、DB 缓存。

3. 短时静态页面

1. 服务器端文件缓存

请求-->处理接口--> 模板渲染 ---> 存储文件---> 响应文件

缓存动态页面,你也能够把生成的文件存到 CDN,而后让 CDN 去响应请求。若是你的请求须要过一些验证,那就把文件存储到服务器,由业务服务器去响应请求。文件还有一个好处是:流。例如:FileReadStream.pipe(ResponseStream)。响应的时候,不须要把文件的内容加载到内存,而是直接用 stream 的方式响应。可是弊端也很多,文件存储,会有并发读写死锁问题。

还有一个问题,分布式系统。可能你有 A、B、C 三个服务器。A 服务器生成了一个文件,还须要实时同步到 B 和 C。固然也可让 A、B、C 挂载同一个磁盘。问题又来了,这个文件要不要备份呢?

2. Redis Cache

请求--> 接口接口---> 模板渲染 --> 存储数据--> 响应 DOM

把请求的 url 当作 key,把模板渲染好的数据当作值,而后根据缓存规则,把数据存储到 redis。

这种小成本的缓存在咱们的系统中有实践,的确大幅提升了系统的响应时间和 QPS,页面的请求大部分是从 redis 读数据,而后返回,单机测试过极限性能,14k QPS。简单描述一下。咱们称之为静态化staticize

  1. 开始请求

  2. 请求校验,filter 等等

  3. 查询缓存 redis

  4. 若是有缓存,则直接响应

  5. 没有缓存,查询数据,从新渲染,存储到 redis.

  6. 响应

  7. 若是需更新缓存,只须要删掉对应的redis 值

4. 动静结合的页面

这种页面在实际状况中更常见。原则:静态页面缓存,动态部分异步请求。

动静结合

静态部分也是模板渲染过来的,浏览器会从 CDN 或者后台缓存中获取到静态页面。页面响应的时间和浏览器的渲染会直接影响用户体验。动态更新的部分通常会在一些细节部分,好比页面的登陆状态。对于全部用户来讲,我看到的这个页面,只有用户头像部分会不一致。若是系统为每一个用户生成一个静态页面成本就过高了,并且彻底不必。

这个页面就变成了:页面 == 短时静态页面 + 局部动态页面。

『用户状态信息』这个特殊的动态内容,还须要用到本地的缓存机制。用户在切换页面的时候,每一个页面都须要动态加载用户信息,因此咱们的作法是在第一次请求到这个信息的时候,存储到 localStorage,而后设置过时时间。退出的时候,主动清理 localStorage。

好比:个性化,我的推荐这种因人而异的板块均可以作成局部动态页面的形式。

5. 数据缓存

以上的方案一样适用于异步请求。

对于CDN 或者其余缓存来讲,缓存不知道你存的内容是 DOM 仍是 JSON,仍是其余格式。它只是帮你存储数据。你一样能够的把,数据接口、局部 DOM 结构(非完整 html 格式)存储到 CDN 或者是 redis 中。好比:页面的配置信息,或者从相关推荐系统请求的 dom 结构。

缓存更新

通常会有主动失效和自动失效缓存机制。

CDN 和 redis 等缓存均可以根据规则设置缓存时间。缓存过时后,会再次获取新的数据。
主动更新通常会用 API 调用方式实现。好比删除 key,或者调用 CDN 接口进行删除操做

clipboard.png

缓存穿透

通常会在第一次请求的时候生成缓存,若是服务器端没有缓存,而后在同一时刻出现高并发请求,请求会直接到达业务逻辑部分,极可能致使系统直接挂掉。

解决办法:

  1. 主动建立缓存。缓存求由系统定时建立。

  2. 请求的时候设置标志位。第一个请求到达,标识这个 url 正在建立缓存,其余请求进入等待队列。

全站 CDN 加速

CDN 动态加速以下图所示:

clipboard.png

例如个人网站有如下接口和页面:

  1. http://www.localhost.com/ // 短时缓存,动静结合

  2. http://www.localhost.com/api/user/1 // 纯动态

  3. http://www.localhost.com/post/hello-world // 永久静态

因此,一、3页面会放到 CDN,2 直接去源站请求。怎么作到呢?

  1. 在 CDN 配置自主源站。意味着请求 CDN 地址的时候,CDN 会去源站请求数据,而后缓存到 CDN 节点。

  2. 设置缓存规则

    / 缓存 1 分钟
    /post/* 缓存 1 年
    /api/ 不设置缓存
  3. cname www.localhost.com 到 CDN 提供的空间域名

clipboard.png

多平台 Mulit Origin

一个 URL 可能会在不一样的平台有不一样的返回和表现形式。

产品的想法都是很完美,一个按钮在不一样的平台会有不一样的显示状态。实际状况很是复杂,在咱们的系统中,出现过一个页面出如今 七 个平台,每一个平台的显示效果会不一致。不论是模板渲染,或者是 js 处理按钮状态等等都是很是复杂的,或者 pc 和移动端页面表现出样式和结构差别。若是还要把这个页面放到缓存,就更加复杂了。

为每一个平台生成一份缓存?能够!

平台的识别来自 UserAgent,不一样的浏览器或者 app,都有不一样的UserAgent。不一样的来源咱们称之为 Origin。Origin + url 就能够生成惟一的 key,去识别惟一的缓存。缓存不限于 redis 和 文件缓存。

CDN 识别来源去读取不一样的文件,就须要 CDN 那边作一些开发工做了。Upyun、七牛这边暂时不支持的。BAT这种大公司他们本身维护的 CDN 就能完美地作到。

另外一种思路:

1个项目,两个域名,2个动态 CDN。PC 和移动端页面分离、接口共享。

例如:为同一个项目配置两个域名:www.localhost.comm.www.localhost.com,同时为这两个域名各设置一个动态 CDN。

由一项目提供两个域名服务,好比:IndexController.main 处理请求 /homepage,移动端和 PC 端的请求路径分别为

  • http://m.www.localhost.com/homepage

  • http://www.localhost.com/homepage

main action 会根据请求来源url,分别渲染不一样的页面。不一样的域名页面,也就被不一样的动态 CDN 缓存起来。

对于 /api/xxxx的接口,天然不须要作 PC 和移动端或者其余平台的区分,一个 action 就能够解决了。这样就避免了维护两套系统的问题。

结语

以上,全站缓存基本完成。

不要凭空去拉高 QPS或者乱用缓存,根据你的业务和实际状况来对待。最重要的事情就是要牢记:保持简洁,按需使用。

参考文献

相关文章
相关标签/搜索