node.js服务器泄漏TCP连接

node.js server leaking TCP connections?

本文关键字:TCP 连接 泄漏 服务器 js node      更新时间:2023-09-26

注:见文末编辑

我有一个node.js (Express)服务器,服务大约15-30个请求/秒。我正在为一堆简单的JADE模板和一个Durandal SPA应用程序提供服务,其中大部分请求都是针对简单的JADE模板的。在几分钟内一切正常,但是服务器在一段时间后开始出现EMFILE错误并最终崩溃。经过一段时间的故障排除后,我发现lsof -i -n -P | grep node的输出包含了大量这样的行:

node    8800 my_user   13u  IPv4 906628      0t0  TCP 172.x.x.x:3000->x.x.x.x:44654 (ESTABLISHED)
node    8800 my_user   14u  IPv4 908407      0t0  TCP 172.x.x.x:3000->x.x.x.x:13432 (ESTABLISHED)
node    8800 my_user   15u  IPv4 908409      0t0  TCP 172.x.x.x:3000->x.x.x.x:38814 (ESTABLISHED)
node    8800 my_user   19u  IPv4 906622      0t0  TCP 172.x.x.x:3000->x.x.x.x:56743 (ESTABLISHED)
node    8800 my_user   20u  IPv4 907221      0t0  TCP 172.x.x.x:3000->x.x.x.x:46897 (ESTABLISHED)
...

我是node.js的初学者,但看起来它无法驳回已经完成的连接,最终导致EMFILE和崩溃。

我已经试过了:

  • ulimit -n 2048:这显然是一个临时解决方案,它延迟了EMFILE错误,但不能解决问题
  • 降低响应超时(默认为2分钟,如果我没记错的话)到更接近5-10秒

有了这两个调整,服务器需要更长的时间才能崩溃,但最终还是会崩溃。即使没有任何负载,它似乎也无法处理"卡住"的TCP ESTABLISHED连接,当请求再次开始到达时,打开的文件描述符的数量不断增加,最终导致进程崩溃。

我的node.js服务器(在coffeescript)看起来像这样(我使用含羞草启动服务器,但我不认为它应该有任何区别):

express = require 'express'
engines = require 'consolidate'
fs      = require 'fs'
http    = require 'http'
https   = require 'https'
options =
    ca: fs.readFileSync __dirname + '/ssl/ca.pem'
    key: fs.readFileSync __dirname + '/ssl/key.pem'
    cert: fs.readFileSync __dirname + '/ssl/cert.pem'
exports.startServer = (config, callback) ->
    app = express()
    app.configure ->
        app.set 'port', config.server.port
        app.set 'views', config.server.views.path
        app.engine config.server.views.extension, engines[config.server.views.compileWith]
        app.set 'view engine', config.server.views.extension
        app.use express.logger({ format: ":date :method :remote-addr :url :response-time" })
        app.use express.favicon __dirname + '/public/favicon.ico'
        app.use express.bodyParser()
        app.use express.methodOverride()
        app.use express.compress()
        app.use express.static(config.watch.compiledDir)
        app.use config.server.base, app.router
    app.configure 'development', ->
        app.use express.errorHandler()
    app.get '/my/route/n1', (req, res) ->
        res.render "./my/template/n1"
    app.get '/my/route/n2', (req, res) -> # route getting the bulk of requests
        res.setTimeout(10000) # timeout introducted attempting to fix the problem
        res.render "./my/template/n2"
    app.get '/my/route/n3', (req, res) ->
        res.render "./my/template/n3"
    app.get '*/?', (req, res) -> res.render 'index'
    server = https.createServer options, app
    server.listen config.server.port, ->
        console.log "Express server listening on port %d in %s mode", server.address().port, app.settings.env
    callback server

我认为node.js不应该有任何问题服务这个数量的请求,所以我认为这是一个错误的配置在我的部分或沿着这些线的东西。我做错了什么?谢谢你!

注::我从lsof的代码/输出中编辑了一堆东西,既是出于隐私考虑,也是因为它应该与问题无关;但是,如果需要任何其他信息,我会更新问题,尽快提供。

编辑:我想我找到了问题的根源。Express用于服务./my/template/n2的连接确实在10秒后超时,但是express.static用于服务图像、css和其他静态资源的连接不会超时(它们是,但是它们需要2-5分钟来释放它们的文件描述符…)。我想我的问题然后减少到:如何设置由express.static服务的文件的响应超时?我试着在其他中间件之前使用app.use express.timeout(10000),但它似乎只适用于主JADE文件,而不适用于图像或css。

我使用Express 3。

在每次其他app.use调用之前添加此中间件后,问题似乎解决了:

        app.use (req, res, next) ->
            res.setTimeout(10000)
            next()

我怀疑这是解决问题的最优雅的方法,但它现在工作得很好。