如何追踪 Node.js 和 Express 的内存泄漏问题?

3
我们使用一个看似简单的Node.js和Express服务器来提供我们的Angular.js项目,您可以在下面看到。其背后的想法是,如果服务器上存在客户端请求的文件,比如main.css,那么它将由express静态中间件返回。否则,我们假设它是一个Angular路由,比如/account,然后返回index.html文件,以便Angular接管。
附图显示了Heroku上的内存峰值,然后Heroku杀死服务器并重新启动。然后又再次出现峰值...
以下是服务器代码:
    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;

有人看出可能导致这个问题的原因吗?使用最新版本的Node 0.10.31和Express 4.8.6。

更新:2014年8月29日

我对server.js文件进行了一些更改,删除了Express的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
很抱歉,我想告诉你的是,除了这些代码之外,没有其他的了。:( - Andrew
感谢更新,Hunter!我也为大家添加了一些更多的数据。 - Andrew
1个回答

5
这一切都要归结于模块“newrelic”。我们在Node上有各种不同编写的应用程序,它们都存在这个问题。无论您使用哪个版本的“newrelic”,1.3.2 - 1.11.4,问题总是相同的,内存会飙升。
只需尝试注释掉require('newrelic')行,你就会发现内存消耗保持稳定。

你知道为什么New Relic会引起问题吗?我希望能够使用New Relic而不看到这个问题。 - silvamerica
这确实是一个New Relic的问题。如果您想要监控,这是线程链接:https://discuss.newrelic.com/t/node-js-agent-memory-leak/9092/26 - Zeno Rocha
1
大家好消息,Newrelic报告称他们在1.18.0版本中修复了泄漏问题:https://github.com/newrelic/node-newrelic/blob/master/NEWS.md#v1180-2015-03-26 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接