讀取靜態檔案會大幅影響伺服器速度,所以要試試看怎麼做file cache。
使用ab做簡單的效能測試
對於檔案系統對應的伺服器,如果每次response都需要讀取一次檔案,速度會很慢,所以加入一個簡單的cache機制來改善效能應該是一個不錯的做法。不過在這之前,先用ab來作效能測試,來得到一些可以比較的數據,才能確保實做cache是有效的。
先針對之前做的index.html做簡單的測試,比較一下讀取檔案造成的效能差距:
* 直接輸出index.html的內容:1824 req/sec
* 讀取後輸出index.html的內容:511 req/sec
看起來的確有影響,接下來就想一下怎麼做cache。
cache構想
利用Javascript的物件,其實就可以做出簡單的cache。
實作檔案系統cache
接下來就把cache機制實作出來吧(lib/cache.js):
var cache = {};
function regist(type) {
if(!cache[type]) {
cache[type] = {};
}
}
function get(type, key) {
if(cache[type] && cache[type][key]) {
return cache[type][key];
}
}
function put(type, key, val) {
if(cache[type]) {
cache[type][key] = val;
}
}
function del(type, key) {
if(cache[type] && cache[type][key]) {
cache[type][key] = null;
delete cache[type][key];
}
}
function query(type, key) {
if(cache[type] && cache[type][key]) {
return true;
} else {
return false;
}
}
module.exports = {
regist: regist,
get: get,
put: put,
del: del,
query: query
};
伺服器部份也需要做更動:
調整過程式以後長這樣(lib/evolve.js):
var http = require('http'),
fs = require('fs'),
url = require('url'),
mime = require('../deps/mime'),
cache = require('./cache');
path = require('path');
var tools = {
getRes: function(respath, dirindex, cb) {
if(cache.query('fscache', respath)) {
cb(false, respath, cache.get('fscache', respath));
} else {
fs.stat(respath, function(err, stats) {
if(err) {
cb(true, respath);
} else {
if(stats.isDirectory()) {
var ic=0;
fs.readFile(path.join(respath, dirindex[ic]), function dih(err, data) {
var tmp = path.join(respath, dirindex[ic]);
ic++;
if(err) {
if(ic<dirindex.length) {
fs.readFile(path.join(respath, dirindex[ic]), dih);
} else {
cb(true, tmp);
}
} else {
var res = {"type": mime.lookup(tmp), "data": data};
cb(false, tmp, res);
cache.put('fscache', respath, res);
}
});
} else {
fs.readFile(respath, function(err, data) {
if(err) {
cb(true);
} else {
var res = {"type": mime.lookup(respath), "data": data};
cb(false, respath, res);
cache.put('fscache', respath, res);
}
});
}
}
});
}
}
};
var evolve = function(conf) {
cache.regist('fscache');
var server = http.createServer(function(request, response) {
var urlObj = url.parse(request.url);
var respath = path.join(conf.basedir, urlObj.pathname);
console.log('request: ' + respath);
tools.getRes(respath, conf.dirindex, function(err, realpath, data) {
if(err) {
console.log(realpath + ' not exists.');
response.writeHead(404, {"Content-Type":"text/html"});
response.end('<h1>404: Request resource not found.</h1>');
} else {
console.log(realpath + ' exists.');
response.writeHead(200, {
"Content-Type": data.type,
"Content-Length": data.data.length
});
response.end(data.data);
}
});
});
this.listen = function(port, addr) {
server.listen(port, addr);
return this;
};
};
module.exports = evolve;
跑一下ab,看看cache是否能改善效能:
* 加入cache機制後讀取index.html內容:1069 req/sec
雖然還是比hello world慢了不少,但是比沒有fs cache之前快了一倍。(在我的MBA上跑,到16000~18000次requests時會發生GC...速度就慢爆了XD,這個數值是只跑到10000次requests)
不過cache是一門大學問,這裡實作的cache機制,並不適合需要提供大量靜態檔案的場合。V8有一些記憶體限制,cache太大的話就會直接爆炸。如果要做好cache,那還需要更複雜的管理機制(例如設定cache容量限制,依照cache使用量來作加權,然後在超過使用量時清除用量低的cache資料等等)
補充一下,在我的MBA上面跑ab -n20000 http://localhost:8443/
,到第16377~16380次request就會停個幾秒鐘。我另外測了記憶體用量,看起來跟GC沒正相關。在windows上跑也沒出現同樣的問題...所以看起來就是Mac版本、作業系統環境這一類的問題,跟node.js不一定相關。