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返回頁面數據。

這樣就做到了頁面靜態化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章