大家都知道用Node.js搭建一個簡單的http服務器是多么easy的事情,打開記事本貼幾句腳本,ctrl+s一下,node server.js 一個http服務器就這樣跑起來了,別看它簡單,但性能絲毫不差。
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Worldn');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
Node.js搭建的服務器性能如此給力確實讓我很好奇它的內部是如何設計的,忍不住翻了翻lib下的代碼。
深入了解過Node.js http模塊的同學應該知道Node.js采用一個純c寫的http_parser來實現對http報文的解析,暴露到Node.js上的是一個HTTPParser對象,在Node.js中用下面一句代碼即可拿到
var HTTPParser = process.binding('http_parser').HTTPParser;
Node.js中調用c/c++內置模塊采用 process.binding('module')模式,比如我們常用的setInterval和setTimeout都是基于c/c++代碼實現,用 process.binding('timer_wrap').Timer即可提供js調用。
Node.js http服務器每收到一個request就會用一個HTTPParser對象來解析出請求信息,比如請求參數,請求體之類的。如果說每接收一個request 都new 一個 HTTPParser對象來處理, 可以想象當并發達到成千上萬時創建HTTPParser對象是多么的頻繁,用完之后又立刻銷毀,這種場景我們很容易想到利用多線程來處理耗時任務,為了避免頻繁的創建銷毀線程對象, 一般都會創建一個線程池來處理任務。于是Node.js中邊產生了對象池這么個東西,也就是接下來要講的freelist 。
首先我們來看看freelist是個什么東西,和對象池有怎樣的聯系。
function FreeList(name, max, constructor) {
this.name = name;
this.constructor = constructor;
this.max = max;
this.list = [];
};
FreeList.prototype.alloc = function() {
return this.list.length ? this.list.shift() :
this.constructor.apply(this, arguments);
};
FreeList.prototype.free = function(obj) {
//debug("free " + this.name + " " + this.list.length);
if (this.list.length < this.max) {
this.list.push(obj);
}
};
代碼相當的簡單,FreeList構造函數接收3個參數,對象池名字,大小以及對象構造函數。比如在Node.js中創建一個httpParse對象池:
var parsers = new FreeList('parsers', 1000, function() {
var parser = new HTTPParser(HTTPParser.REQUEST);
parser._headers = [];
parser._url = '';
parser[kOnHeaders] = parserOnHeaders;
parser[kOnHeadersComplete] = parserOnHeadersComplete;
parser[kOnBody] = parserOnBody;
parser[kOnMessageComplete] = parserOnMessageComplete;
return parser;
});
Node.js中用這段代碼創建了一個叫parsers,大小為1000的對象池,當Node.js服務器接收到一個request時便向這個對象池索取一個HTTPParser對象即調用對象池parsers的alloc方法,此時便拿到了一個parser對象,parser對象解析完http報文后node并沒有立即釋放它,而是將它重新放入對象池parsers中,即調用parsers.free(parser),當然了只有當池子還沒滿的時候才可以重新被放進去。如此便實現了parser對象的重復利用,當并發數很高時極大的提升性能。

相信小伙伴們應該都很清楚對象池的原理以及它在Node.js服務器中的作用了, 希望對大家在實際的業務中有所幫助哦~
參考文檔
1.https://github.com/joyent/node/tree/master/deps/http_parser
2.https://github.com/joyent/node/blob/master/lib/freelist.js