node.js icon indicating copy to clipboard operation
node.js copied to clipboard

Node.js 之对象池

Open hustxiaoc opened this issue 10 years ago • 9 comments

大家都知道用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对象的重复利用,当并发数很高时极大的提升性能。

Alt text

相信小伙伴们应该都很清楚对象池的原理以及它在Node.js服务器中的作用了, 希望对大家在实际的业务中有所帮助哦~

参考文档

1.https://github.com/joyent/node/tree/master/deps/http_parser 2.https://github.com/joyent/node/blob/master/lib/freelist.js

hustxiaoc avatar May 14 '14 08:05 hustxiaoc

thanks!

zhangxiaov avatar Jan 11 '15 13:01 zhangxiaov

@zhangxiaov 不客气~

hustxiaoc avatar Jan 12 '15 13:01 hustxiaoc

@hustxiaoc 看起来非常不错,我是个新手,目前也遇到并发问题,不知道如何能将其应用到实际场景中。

scfobao avatar Dec 03 '16 08:12 scfobao

@scfobao 你是什么场景呢

hustxiaoc avatar Dec 05 '16 03:12 hustxiaoc

@hustxiaoc 新手路过,照着书上的例子,愣是什么都没弄出来,到处报错,郁闷呀,跪求大神带我飞飞飞...

onszzo avatar Jan 23 '17 10:01 onszzo

@onszzo 可以贴下你的代码么

hustxiaoc avatar Mar 22 '17 09:03 hustxiaoc

虽然目前没用到。但还是不得不赞一个。

MsWangyouhui avatar Jun 07 '17 01:06 MsWangyouhui

good!

vibing avatar Dec 18 '17 02:12 vibing

谢谢分享啊,新手程序员留

fushome avatar Feb 26 '18 12:02 fushome