7.关于cdn、页面静态化

我们之前一直都在介绍动态请求的加速,接下来讲一下静态请求,也就是cdn。

当请求来到服务器上时,如果是访问静态资源,那么就将请求解析到cdn加速域名中,再由cdn(海量的就近加速节点)就近看有没有存静态资源,有的话直接返回,没有的话去指定的http地址中抓取数据返回并缓存起来,下次就可以直接返回了。

接下来配置一下,这里用的是阿里云的:

在cdn上添加配置,ip就是nginx的ip:

接下来在域名解析中添加cname的记录:

访问即可,压测:


cache control响应头

随便找一个网站看下参数,可以看到cache control是响应头返回的。

他的作用是服务端告诉客户端这个http的response可不可以缓存,以什么样的策略缓存。他有几种状态:

private:客户端可以缓存

public:客户端和代理服务器(例如nginx、cdn等)都可以缓存

max-age=xxx:缓存的内容将在xxx秒后失效

no-cache :强制向服务端再验证一次,这个的意思是客户端会缓存起来,但是下次请求的时候会向服务端验证这个缓存能不能用,再决定是否用这个缓存

no-store:不缓存请求的任何返回内容

可以看下图:

什么是再验证一次?也就是有效性判断,那么怎么进行有效性判断?

ETag:他是资源的唯一标识,一般都是将请求的资源进行MD5或者hash等生成一个唯一标识,在第一次响应的时候加上这个标识,浏览器会存储下这个ETag,下次请求是将这个ETag的值传给服务器,服务端会把这个值和本地的ETag作比较,如果一致就返回304证明有效。

If-None-Match:客户端发送的匹配ETag标识符

Last-modified:服务端响应的时候返回的资源最后被修改的时间的标识符

If-Modified-Since:客户端发送的匹配资源最后修改时间的标识符,早于Last-modified说明无效,晚于Last-modified说明有效

那么大概流程就是:


浏览器刷新的三种方式

回车刷新或超链接:看cache-control对应的max-age是否仍然有效,有效直接取cache数据,如果cache-control中为no-cache,则进入缓存协商逻辑。

F5刷新:去掉cache-control中的max-age或直接设置max-age为0,然后进入缓存协商逻辑

ctrl+F5:去掉cache-control和协商头,强制刷新

所谓的协商机制就是比较Last-modified和ETag到服务端,若服务端判断没变化则返回304不返回数据,否则200返回数据。

当然,这里说的都是请求静态资源,动态资源不会存ETag。


cdn自定义缓存策略

为什么要讲这么多cdn?因为cdn作为一个中间件,对于客户端来说充当服务端的角色,对于服务端来说充当客户端的角色。

那么对于cdn的缓存策略,他可以自定义目录过期时间。可自定义后缀名过期时间。可自定义对应权重,也就是说前两个哪个权重大就遵循哪个。可以通过界面或api强制cdn对应目录刷新(非保成功),忽略上面的规则。

这是阿里云提供给我们的请求静态http的方式:如果原站没有配置,遵从阿里云自己的。如果原站有配置,遵从控制台配置的。控制台没有配置,遵从原站自己的。


静态资源部署策略

在工作中经常会出现一个场景:一些css、js、img等元素更新了后台,但是前台没有更新?清缓存重新请求。

解决这种问题,第一我们可以使用版本号,例如:a.js?v=1.0。当然这种方式非常不便利,且维护困难。这些js、css都是嵌在html文件中的,所以html文件一定要设置成no-cache或是强推。一般不用这种。

第二我们可以对于这些元素使用摘要部署,例如a.js?v=45edw,只要文件不变,version就不会变。但是存在先部署html还是先部署资源的覆盖问题。一般也不用这种。

第三种我们使用摘要做文件名部署,例如45edw.js,新老版本并存且可回滚,资源部署完后再部署html。这样新老版本就不会产生冲突了。这样生成的静态资源可以保持生命周期内不会变,max-age可以设置很长,无视失效更新周期。甚至cnd都可以设置很长,而且不用回源。对于html文件要设置成no-cache或是较短的max-age,以便于更新。如果html想设置很长的max-age,可以依靠动态的获取版本号请求发送到后端,异步下载最新的版本号html后展示渲染在前端。

对于动态请求:如果即使做了多级缓存,仍然有压力,那么我们可以考虑把动态请求转化成json以静态化资源推送到cdn上。可以依靠异步请求获取后端节点对应资源状态,如果产生了变化,就再更新前端数据。也可以通过跑批紧急推送cdn内容来做下架操作。因此有了下面的操作:


全页面静态化

不管系统内部动态请求也好、cdn也好、nginx也好,对于用户来说看到的都是一个html页面。既然htm、css、js静态资源可以cdn化,js、ajax动态请求也可以cdn化,那么我们也可以做全页面静态化。

定义:在服务端完成html、css甚至js的load,并且渲染成纯html文件后,直接以静态资源的方式部署到cdn上。

要实现这个,我们需要一个库:phantomjs,可以理解为无头浏览器,模拟浏览器的功能,借助其模拟内部的webkit js的执行。

用起来也很方便,下载好以后,windows下:把bin加到环境变量中,然后在example下直接运行phantomjs hello.js即可。

linux下更方便,直接bin/phantomjs examples/hello.js即可。

再看一个官方示例:

var page = require('webpage').create();
page.open('http://baidu.com', function() {
    setTimeout(function() {
        page.render('baidu.png');
        phantom.exit();
    }, 200);
});

运行后会产生一个图片:

为什么要延迟200毫秒执行?因为虽然html网页执行了,但是js可能还没执行完。这就是phantomjs最简单的示例。

那么我们怎样使用phantomjs呢?首先我们需要修改全页面静态化的实现,采用initView和hasInit方式防止多次初始化。其次编写轮询生成内容方式。最后将全静态化页面生成后推送到cdn上。

实战一下:

我们需要把页面上的ajax请求在服务端或爬虫端执行掉,完成后依赖于爬虫生成一个已经reloadDom完成的静态文件,然后部署到cdn上完成全页面静态化操作。

首先新建一个js:

var page = require("webpage").create();
var fs = require("fs");
page.open("http://120.79.48.0//resources/getitem.html?id=6",function(status){
	console.log("status = " + status);
	fs.write("getitem.html",page.content,"w");
	phantom.exit();
});

爬一下,打开生成的html:

我们可以看到数据并没有出来,而且也没有css样式。没有样式是因为本地文件并没有对应的css包,所以单独运行html没有用,所以需要将对应资源放到服务器上。至于数据没出来,是因为还是发送的ajax请求,数据没有做到静态化,原因就是因为爬虫爬的时候页面已经渲染了,但是ajax数据还没有收到。所以改进下代码:

var page = require("webpage").create();
var fs = require("fs");
page.open("http://120.79.48.0//resources/getitem.html?id=6",function(status){
	console.log("status = " + status);
	setTimeout(function(){
		fs.write("getitem.html",page.content,"w");
	phantom.exit();
	},1000);
});

看下html:

而原生html中是会有document.ready的,那么在爬虫爬后,ready还是会在页面打开的时候执行里面的ajax请求,所以我们需要对代码进行调整:

可以加一个隐藏域,然后通过控制变量来决定是否执行下面的代码。

修改下爬虫js:

var page = require("webpage").create();
var fs = require("fs");
page.open("http://120.79.48.0//resources/getitem.html?id=6",function(status){
	console.log("status = " + status);
	var isInit = "0";
	setInterval(function(){
		if(isInit != "1"){
			page.evaluate(function(){
				initView();
			});
			isInit = page.evaluate(function(){
				return hasInit();
			});
		}else{
			fs.write("getitem.html",page.content,"w");
			phantom.exit();
		}
	},1000);
});

第一次触发定时器,调用前端的initView()方法,然后更改数据,当页面请求都完成了,进入else返回页面数据。

这样就做到了页面静态化。

发布了97 篇原创文章 · 获赞 28 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/haozi_rou/article/details/105375462