大家都知道用Node.js搭建一个简单的http服务器是多么简单的事情,打开记事本贴几句脚本,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
该文章来自于阿里巴巴技术协会(ATA)
作者:淘杰