2014-08-28 96 views
2

我们正在使用看似简单的Node.js和Express服务器来提供我们的Angular.js项目,您可以在下面看到。它背后的想法是,如果客户端要求的服务器上存在一个文件,例如main.css,则它将由快速静态中间件返回。否则,我们假设它是一个类似于/ account的角度路线,我们返回index.html文件,以便Angular可以接管。如何追踪Node.js和Express内存泄漏?

附加,你可以看到在Heroku上的内存尖刺,然后Heroku杀死服务器并重新启动它。那么它的尖刺再次...

Memory pattern

这里是服务器:

var newrelic = require('newrelic') 
     , fs = require('fs') 
     , express = require('express') 
     , app = express() 
     , ENV = process.env.NODE_ENV || 'development' 
     , ENV_PROD = ENV == 'production' 
     , port = process.env.PORT || 8080; 

    console.log('ENV: ' + ENV); 

    // force ssl 
    app.use(function(req, res, next){ 
     if (ENV_PROD) { 
      var protocol = req.get('x-forwarded-proto'); 
      if (protocol != 'https') { 
       res.redirect(301, 'https://' + req.get('host') + req.url); 
      } else { 
       res.header('Strict-Transport-Security', 'max-age=31536000'); 
       next(); 
      } 
     } else { 
      next(); 
     } 
    }); 

    // set default headers 
    app.use(function(req, res, next){ 
     var cache = ENV_PROD ? 3600 : 0; 
     res.set('Cache-Control', 'public, max-age='+cache); 
     res.set('Vary', 'Accept-Encoding'); 
     next(); 
    }); 

    // static cache headers 
    app.use('/bower_components*', function(req, res, next){ 
     res.set('Cache-Control', 'public, max-age=2419200'); 
     next(); 
    }); 

    // set static directory 
    app.use(express.static(process.cwd() + '/build')); 

    // deeplink 
    app.use('/dl/*', function(req, res, next){ 
     var path = req.baseUrl.replace('/dl/', ''); 
     var url = 'https://www.deeplink.me/tablelist.com/' + path; 
     return res.redirect(url); 
    }); 

    // file not found, route to index.html 
    app.use(function(req, res, next){ 
     res.sendFile(process.cwd() + '/build/index.html'); 
    }); 

    // error handling 
    app.use(function(err, req, res, next){ 
     res.status(404).send(err.message); 
    }); 

    app.listen(port, function() { 
     console.log('Listening on port: ' + port); 
    }); 

    module.exports = app; 

有谁看到任何脱颖而出,可能会导致此?它采用了最新版本的节点0.10.31和快速4.8.6

更新:2014年8月29日

我做了一些改动server.js文件,删除快捷的SENDFILE和静态中间件。你可以看到下面的更新图表,看起来更好,但使用的内存比我个人喜欢的要多。更新的服务器也在下面。

Updated memory usage

var newrelic = require('newrelic') 
     , fs = require('fs') 
     , express = require('express') 
     , app = express() 
     , ENV = process.env.NODE_ENV || 'development' 
     , ENV_PROD = ENV == 'production' 
     , SSL = ENV_PROD || process.env.SSL 
     , port = process.env.PORT || 8080 
     , config = require('../config')[ENV]; 

    console.log('ENV: ' + ENV); 

    // force ssl 
    app.use(function(req, res, next){ 
     if (SSL) { 
      var protocol = req.get('x-forwarded-proto'); 
      if (protocol != 'https') { 
       res.redirect(301, 'https://' + req.get('host') + req.url); 
      } else { 
       res.header('Strict-Transport-Security', 'max-age=31536000'); 
       next(); 
      } 
     } else { 
      next(); 
     } 
    }); 

    // set default headers 
    app.use(function(req, res, next){ 
     var cache = ENV == 'production' ? 3600 : 0; 
     res.set('Cache-Control', 'public, max-age='+cache); 
     res.set('Vary', 'Accept-Encoding'); 
     next(); 
    }); 

    // static cache headers 
    app.use('/bower_components*', function(req, res, next){ 
     res.set('Cache-Control', 'public, max-age=2419200'); 
     next(); 
    }); 

    // add isFile to req 
    app.use(function(req, res, next){ 
     var fileName = req.path.split('/').pop(); 
     var isFile = fileName.split('.').length > 1; 
     req.isFile = isFile; 
     next(); 
    }); 

    // static 
    app.use(function(req, res, next){ 
     if (req.isFile) { 
      var fileName = req.path.split('/').pop(); 
      var path = process.cwd() + '/build' + req.path; 
      fs.readFile(path, function(err, data){ 
       if (!data) return next(); 
       res.contentType(fileName); 
       res.send(data); 
      }); 
     } else { 
      next(); 
     } 
    }); 

    // file not found, route to index.html 
    app.use(function(req, res, next){ 
     if (req.isFile) { 
      next(new Error('Not found')); 
     } else { 
      var path = process.cwd() + '/build/index.html'; 
      fs.readFile(path, function(err, data){ 
       if (err) return next(err); 
       res.contentType('index.html'); 
       res.send(data); 
      }); 
     } 
    }); 

    // error handling 
    app.use(function(err, req, res, next){ 
     res.status(404).send(err.message); 
    }); 

    // start server 
    app.listen(port, function() { 
     console.log('Listening on port: ' + port); 
    }); 

    module.exports = app; 
+1

我没有看到你到目前为止显示的代码中的任何内容,会导致内存泄漏。 – mscdex 2014-08-28 14:31:47

+0

我想告诉你,有更多的代码,但这就是字面上:( – Andrew 2014-08-28 14:47:43

+1

嗨安德鲁 - 你不是唯一的用户体验一个奇怪的记忆问题,我们正在追查的根本原因,但最终似乎那个节点没有运行GC,早期的手动运行global.gc()的测试修复了这个问题(但是当然你不应该*必须这样做) – hunterloftis 2014-08-29 19:07:38

回答

5

这一切都是因为模块的 'NewRelic的'。我们在Node上有各种不同的应用程序,并且都有问题。无论您使用的是什么版本的'newrelic',1.3.2 - 1.11.4,问题总是一样的,内存只是爆炸。

只需尝试对require('newrelic')行进行评论,就可以看到内存消耗保持不变。

+0

你知道为什么New Relic造成这个问题吗?我想能够使用New Relic而不是看到这个问题。 – silvamerica 2014-12-04 23:58:13

+0

这确实是一个New Relic问题,如果你想要的话以监控:https://discuss.newrelic.com/t/node-js-agent-memory-leak/9092/26 – 2015-03-10 12:31:19

+0

嘿家伙好消息,纽瑞克斯报告他们修复了版本1.18.0中的泄漏:https:// github.com/newrelic/node-newrelic/blob/master/ NEWS.md#v1180-2015-03-26 – Andrew 2015-03-30 00:24:43