即使可以與檔案系統對應,對於一般使用上還是非常不足的。除了靜態網頁,還需要一個backend來處理資料,才是一個比較完整的伺服器。
進一步開發的需求
node.js的request事件處理函數,可以接收到ServerRequest物件,裡面包含了完整的http request資訊。利用這些資訊,其實可以制定出簡單的規則,讓伺服器知道什麼時候要回應靜態檔案,什麼時候需要用程式處理資訊。一個典型的http request大概會長的像這樣:
GET /index.html HTTP/1.1
Host: www.w3c.org
......
在node.js的http.ServerRequest物件可以找到這些資訊(利用之前寫的probe程式來取得API資訊):
上圖中可以利用的資訊:
簡單的router機制
有了這些資訊,就可以制定出一套簡單的規則:
為了使用方便,可以考慮使用fluent interface的方式來設計:
實作router
程式比較複雜,需要整合router與之前的檔案系統對應機制,先寫好一個獨立的router機制,用來依照條件存放各個route的處理函數,以及不同host所需對應到不同路徑的檔案系統目錄。
router.js
var path = require('path'),
cache = require('./cache');
function addhost(host) {
host = host || 'localhost';
cache.regist(host);
cache.put(host, 'fs', {});
cache.put(host, 'route', {});
}
function addfs(host, path, fs) {
if(path.indexOf('/')!==0) path = '/' + path;
var a = cache.get(host, 'fs');
if(a) {
a[path] = fs;
}
}
function addroute(host, path, method, fn) {
if(path.indexOf('/')!==0) path = '/' + path;
var a = cache.get(host, 'route');
if(a) {
if(!a[path]) a[path] = {};
a[path][method] = fn;
}
}
function query(host, dir, method) {
if(dir.indexOf('/')!==0) dir = '/' + dir;
var a = cache.get(host, 'route');
if(a && a[dir] && a[dir][method]) {
return {
type: 'route',
result: a[dir][method]
};
}
var a = cache.get(host, 'fs');
if(a) {
for(var i in a) {
if(dir.indexOf(i)===0) {
return {
type: 'fs',
result: path.join(a[i], dir.substr(i.length, dir.length))
};
}
}
}
}
module.exports = {
addhost: addhost,
addfs: addfs,
addroute: addroute,
query: query
};
接下來,還需要調整evolve.js:
(程式碼太長,請到github看囉)
https://github.com/fillano/evolve/blob/eba643263f2c2b306c38deab7a2b5b082ca97845/lib/evolve.js
在evolve.js中,加入了一個dispatch函數,利用router.query()來查詢request url的route,如果是定義好的route handler,就交給它處理,否則交給getRes處理。(為了測試記憶體使用量,所以稍微調整了輸出資訊,用ab -n20000 http://localhost:8443/
來測試可以看到v8做gc使得記憶體使用量上上下下變化)
使用router
實做時又調整了一下使用規則,傳入一個callback來處理要輸出的結果,這樣跟昨天寫好的程式就可以搭配:
var evolve = require('../lib/evolve');
var path = require('path');
var server = new evolve({
dirindex: ['index.html', 'index.htm', 'default.htm']
});
server.host('localhost:8443')
.map('/', path.join(__dirname, '../www'))
.get('/hello', function(request, response, cb) {
cb(false, '/hello', {
type: 'text/html',
data: 'hello'
});
});
server.listen(8443, 'localhost');
使用起來應該還不會太複雜,而且使用ab測試了一下,跟昨天的效能差不多。
不過問題是,伺服器程式本身越來越複雜,也越來越難測試。為了能進一步開發,必須有比較好的方法,才能一步一步前進...這個就留到明天來想一想。