NodeJs的性能问题

时间:2011-05-06 03:07:27

标签: performance mongodb node.js mongoose

我正在使用NodeJ构建实时统计应用程序。对于原型,我在RackSpace服务器中使用四核AMD Opteron,使用Cluster NodeJs(http://learnboost.github.com/cluster/)和使用本机nodejs驱动程序的MongoDb对nodejs服务器进行测试。

基本上我在公司项目中插入了一个JS代码,为一堆客户的网站提供内容。这段代码“ping”我的服务器每10秒调用一个图像并传递我在服务器端获得的参数并在MongoDb集合中插入(或更新)。在一天的“慢”时间里,我得到大约3000个连接(我在终端上使用netstat -natp命令获得这些连接)每次使我的集群使用大约25%的每个核心(我使用“top”命令获得这些)。但是在一个“忙碌”的时刻,我每次获得大约7000多个连接,这使得我的集群变得疯狂(大约80%+使用每个核心),而且随着时间的推移,节点似乎会降级。 这是正常的吗?或者Nodejs应该以更“简单”的方式处理这些命中?如果我使用Mongoose,性能会增加吗?

如果你对MongoDb感到好奇,它会占用一个核心的4%左右,这对我来说很好(没有使用索引,使用率约为50%+,但至少,索引解决了这个性能问题)。

非常感谢耐心, 欢呼声。

编辑:

使插入的代码如下所示:     db.open(function(err,db){});

return connect.router(function(app){
    app.get("/pingserver/:clientid/:event/:cachecontrol", function(req, res, next){
    event:'+req.params.event + ', cachecontrol:' + req.params.cachecontrol);
        var timestamp = new Date(); 
          switch(req.params.event) {
          case 'load':
              var params = url.parse(req.url, true).query;

              db.collection('clientsessions', function(err, collection)         {
                try {

                    var client = {
                        id: req.params.clientid,
                        state: req.params.event + 'ed',
                        loadTime: timestamp.getTime(),
                        lastEvent: req.params.event,
                        lastEventTime: timestamp.getTime(),
                        lastEventDate: timestamp.toString(),
                        events: [{
                            event: req.params.event,
                            timestamp: timestamp.getTime(),
                            date: timestamp.toString()
                        }],
                        media: {
                            id: params.media.split('|')[0] || null,
                            title: unescape(params.media.split('|')[1]) || null
                        },
                        project: {
                            id: params.project.split('|')[0] || null,
                            name: unescape(params.project.split('|')[1]) || null
                        },
                        origin: req.headers['referer'] || req.headers['referrer'] || '',
                        userAgent: req.headers['user-agent'] || null,
                        userIp: req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)),
                        returningUser: false
                    };
                }catch(e) {console.log(e);}       
                 collection.insert(client, function(err, doc) {
                 });
              });
              break;

          case 'ping':
              db.collection('clientsessions', function(err, collection) {
                  collection.update({id: req.params.clientid}, { 
                                                     $set : { lastEvent: req.params.event 
                                                             ,lastEventTime: timestamp.getTime(),lastEventDate: timestamp.toString()}
                                                   }, {}, function(err, doc) {});
              });
              break;

          default:
              db.collection('clientsessions', function(err, collection) {
                  collection.update({id: req.params.clientid}, { 
                                                     $set : {state: req.params.event+'ed'
                                                            , lastEvent: req.params.event 
                                                            , lastEventTime: timestamp.getTime()}
                                                   , $push : { events : { event: req.params.event, timestamp: timestamp.getTime(), date: timestamp.toString() } } }, {}, function(err, doc) {});
              });

              break;
          }

          if (!transparent) {
              console.log('!transparent');
              transparent = fs.readFileSync(__dirname + '/../../public/images/transparent.gif', 'binary');
          }
          res.setHeader('Content-Type', 'image/gif');
          res.setHeader('Content-Length', transparent.length);

          res.end(transparent, 'binary');
      });
});

6 个答案:

答案 0 :(得分:2)

  

这是正常的吗?

取决于,他们自己的联系会消失吗?他们只是继续建设吗?你在谈论“网络连接”(http)或MongoDB连接吗?

mongod日志说什么? node日志说什么?

您每秒收到多少请求?

  

或者Nodejs应该以更“简单”的方式处理这些命中?

很难说不知道代码在做什么。

您希望盒子处理多少个同时连接?

  

如果我使用Mongoose,性能会提高吗?

所以Mongoose实际上是node-mongodb-native驱动程序的对象包装器。它不是一个不同的驱动程序,它只是一个包装器。

包装器将代码添加到您已有的代码中。如果您遇到代码问题,则无法保证添加代码可以更好地解决问题。如果mongoose确实解决了你的问题,那么它正在做一些与你不相关的事情。如果是这种情况,您不一定需要Mongoose,您只需要更好的连接管理。


看看有很多潜在的问题来源。

解决这个问题的唯一方法就是打破碎片并深入挖掘细节。开始的地方:   - 正确关闭MongoDB的连接(查看数据库日志)?   - 日志是否包含任何其他错误?   - 为节点日志做同样的事情?   - 你有关于内存使用情况的图表吗?谁占用了最多的记忆?   - 当你达到每个核心的80%时,哪个进程正在执行此操作? mongodnode?别的什么?

为了真正帮助你,我们需要更多关于系统发生情况的数据。

答案 1 :(得分:1)

连续请求可能非常昂贵,尤其是当它们之间的超时很小时。在您的情况下,您接受每秒约300-700 +并发请求,您的系统负载可能取决于您正在处理什么。您可以尝试切换到Mongoose,但是如果它适用于您的场景,我宁愿查看图像处理和缓存,因为DB似乎不是您的瓶颈(尽管DB驱动程序也可能是问题)。

答案 2 :(得分:1)

if (!transparent) {
          console.log('!transparent');
          transparent = fs.readFileSync(__dirname + '/../../public/images/transparent.gif', 'binary');
      }

透明假的频率是多少?我没有看到它在代码中定义。您正在阻止同步磁盘IO上的整个Node进程,可能对每个请求都是如此。为什么?如果必须从磁盘读取文件,请异步执行。如果文件是静态的和小的,也许你应该把它加载到内存中一次。

答案 3 :(得分:1)

节点的http服务器默认保持活动状态。在您的情况下,它会导致太多无用的连接。只需尝试向disable Keep-Alive添加标头 - 具有群集的普通节点就可以了。

res.setHeader("Connection", "close")

答案 4 :(得分:0)

只是更新:

我已经删除了群集并在服务器上放置了Nginx层。因此,“降级”需要花费更长的时间,但它仍在这样做,特别是消耗了大量的系统内存。 有什么想法吗?

非常感谢所有答案!

编辑: 重新做了一些测试。我认为主要问题是关于开放的联系。当我在Nginx端口上运行netstat时,它就像2000连接一样。当我在每个nodejs应用程序端口上运行时,它会显示2000(或更多)。基本上我的“最佳情况”是nodejs应用程序上的打开连接的总和将匹配Nginx端口上的打开连接,对吧?我认为这是主要问题,它正在影响巨大的“time_wait”状态。

答案 5 :(得分:0)

你可能也想在缓冲区中提供透明的gif,如下所示:

https://gist.github.com/657246#comments