【问题标题】:Nodejs with Express and CORS Hanging after so many calls带有 Express 和 CORS 的 Nodejs 在多次通话后挂起
【发布时间】:2015-10-31 06:33:35
【问题描述】:

所以我有一个 Node.JS REST API,我在其中添加了 CORS npm 模块。现在我发现打了这么多电话后,服务器会挂起,我必须重新启动它。而且它的调用次数也很少,比如 6 或 7。我尝试使用默认设置的 CORS npm 模块,我也尝试像这样手动设置:

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
})

但无论我采用哪种方式,结果都是一样的。任何想法将不胜感激。

这是我的 main.js 文件的代码

// Imports
var express  = require('express'),
    CORS = require('cors'),
    app = express(),                               // create our app w/ express
    verRouter = express.Router(),                  // Create router for version stuff
    stageRouter = express.Router(),                // Create router for version stuff
    morgan = require('morgan'),                    // log requests to the console (express4)
    bodyParser = require('body-parser'),           // pull information from HTML POST (express4)
    methodOverride = require('method-override'),   // simulate DELETE and PUT (express4)
    winston = require('winston'),                  // logging library
    mysql = require('mysql'),                      // mysql library
    jwt = require('jsonwebtoken'),                 // library to create JSON tokens for authentication
    passport = require('passport'),                // Authentication library
    LdapStrategy = require('passport-ldapauth'),   // specific library for LDAP use
    config = require('./config/config')            // config to hold sensitive info like mysql credentials and secret key

//init logger
var logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)(),
    new (winston.transports.File)({ filename: 'versions_api.log' })
  ]
})
logger.level = 'debug'
logger.exitOnError = false

//LDAP Authentication
var eaOPTS = {
  server: {
    url: config.ldap.url,
    bindDn: config.ldap.bindDn,
    bindCredentials: config.ldap.bindCredentials,
    searchBase: config.ldap.searchBase,
    searchFilter: config.ldap.searchFilter,
    groupSearchBase: config.ldap.groupSearchBase,
    groupSearchFilter: config.ldap.groupSearchFilter,
    groupSearchAttributes: config.ldap.groupSearchAttributes
  }
}

passport.use(new LdapStrategy(eaOPTS))

// db configuration
var pool = mysql.createPool({
  connectionLimit : 10,
  host            : config.db.host,
  user            : config.db.user,
  password        : config.db.password,
  database        : config.db.database
})

app.use(CORS(corsOpts))
app.use(morgan('dev'))                                          // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'}))             // parse application/x-www-form-urlencoded
app.use(bodyParser.json())                                      // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' }))  // parse application/vnd.api+json as json
app.use(methodOverride())

//Import the routes
var versions = require('./routes/versions')(app, pool, logger, express, jwt)
var states = require('./routes/states')(app, pool, logger, express, jwt)
var auth = require('./routes/auth')(app, logger, express, passport, jwt)


//Set up the routes
app.use('/versions', versions)
app.use('/states', states)
app.use('/auth', auth)

//Catch any 404s and print out their error message.
app.use(function(err, req, res, next) {
  if(err.status !== 404) {
    return next()
  }

  res.status(404)
  logger.error(err.message)
  console.log(err.message)
  res.send(err.message)
})

// listen (start app with nodemon main.js)
var port = process.env.PORT || 8888
app.listen(port, function() {
  console.log("App listening on port "+port)
})

谢谢!

更新: 根据要求,这里是versions.js 和state.js 文件:

/*************************************************************************************
VERSIONS.JS
Description: Module holding all routes for the versions section of the Version REST
            API. All functions in this module handle the versions. Add a new version,
            get all versions, get a single version, update a version, search versions.
*************************************************************************************/
module.exports = function(app, pool, logger, express, jwt) {
  var helper = require("../modules/helpers")(logger)
  var router = express.Router()

  // get all versions
  router.get('/list', function(req, res, next) {
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT *, DATE_FORMAT(st.launch_date, '%Y-%m-%d %H:%i:%s') AS launch FROM versions v LEFT JOIN state_transition st ON (st.version_id=v.id)", function(err, vers) {
        if (err) return helper.handleErr(err, res)

        res.json(vers) // return all versions in JSON format
      })
    })
  })

  // create a version and send back all versions after creation
  router.post('/add', function(req, res, next) {
    var vals = req.body
    if(!('comment' in vals) || !('version' in vals)) {
      return helper.createErr("Comment and Version are required parameters.", res)
    }
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT * FROM versions WHERE version='"+vals.version+"'", function(err, ver) {
        if(ver.length) helper.createErr("The version "+vals.version+" already exists.")
      })
      conn.query("INSERT INTO versions(version,comment) VALUES('"+vals.version+"','"+vals.comment+"')", function(err, ver) {
        if (err) return helper.handleErr(err, res)
        logger.info("New version added. Version: '"+vals.version+"' Comment: '"+vals.comment+"'")
        res.json({"status":"ok", "message":"Version added."})
      })
    })
  })

  // Retrieve a single version
  router.get('/:ver_id', function(req, res, next) {
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT *, DATE_FORMAT(st.launch_date, '%Y-%m-%d %H:%i:%s') AS launch FROM versions v LEFT JOIN state_transition st ON (st.version_id=v.id) WHERE v.id="+req.params.ver_id, function(err, ver) {
        // if there is an error retrieving, send the error.
        if (err) return helper.handleErr(err, res)
        if (ver.length){
          res.json(ver)
        } else {
          return helper.createErr("No results found.", res)
        }
      })
    })
  })

  // Update a version by ID
  router.post('/edit/:ver_id', function(req, res, next) {
    var vals = req.body
    if (!req.params.ver_id) {
      return helper.createErr("Must provide a version id.", res)
    }

    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT * FROM versions WHERE id="+req.params.ver_id, function(err, ver) {
        if (err) return helper.handleErr(err, res)
        //Update the comment for the version since this is the only thing about a version we can update.
        if (ver.length) {
          conn.query("UPDATE versions SET comment='"+vals.comment+"' WHERE id="+req.params.ver_id, function(err) {
            if (err) return helper.handleErr(err, res)
            conn.query("SELECT * FROM versions WHERE id="+req.params.ver_id, function(err, newver) {
              logger.info('Version updated from '+JSON.stringify(ver[0])+' to '+JSON.stringify(newver[0]))
              res.json({"status":"ok", "message":"Version updated."})
            })
          })
        } else {
          return helper.createErr("No results found.", res)
        }
      })
    })
  })

  //Get all versions that match the provided search criteria
  router.post('/search', function(req, res, next) {
    //version: *.*.*.***** - Search for any version that matches up to the maximum number of characters (a full version string)
    //state: Search for versions and the appropriate state transitions that match the state
    //date_start: Search for versions that have state transition(s) from the given start date (to now if no end date provided)
    //date_end: Search for version that have state transition(s) to a given end date (from a start date if provided, otherwise from the beginning of time)
    var vals = req.body
    if (Object.keys(vals).length == 0) {
      //Create an error cause we can't search for nothing.
      return helper.createErr("Must provide at least one search parameter (version, state, date start, or date end).", res)
    } else {
      pool.getConnection(function(err, conn) {
        if (err) return helper.handleErr(err, res)

        //Initialize the query and then append to it as required
        q = "SELECT *, DATE_FORMAT(st.launch_date, '%Y-%m-%d %H:%i:%s') AS launch FROM versions v LEFT JOIN state_transition st on (v.id = st.version_id) WHERE"
        if ('version' in vals) {
          q = helper.addWhere(q, " v.version LIKE '"+vals.version+"%'")
        }
        if ('state' in vals) {
          q = helper.addWhere(q, " st.state="+vals.state)
        }

        if ('date_start' in vals && !('date_end' in vals)) {
          q = helper.addWhere(q, " DATE(st.launch_date) > '"+vals.date_start+"'")
        } else if (!('date_start' in vals) && 'date_end' in vals) {
          q = helper.addWhere(q, " DATE(st.launch_date) < '"+vals.date_end+"'")
        } else if ('date_start' in vals && 'date_end' in vals) {
          q = helper.addWhere(q," DATE(st.launch_date) BETWEEN '"+vals.date_start+"' AND '"+vals.date_end+"'")
        }

        conn.query(q, function(err, vers) {
          if (err) return helper.handleErr(err, res)
          if (vers.length) {
            res.json(vers)
          } else {
            res.json({"results": "No results found."})
          }
        })
      })//end of connection object retrieved from pool
    }
  }) //end of get "search" request)

  return router
}//end module

/************************************************************************************
STATES.JS
Description: Module holding all routes for the state section of the Version REST API.
            All functions in this module handle the states. Add a new state, get all
            states for a given version, get a list of the states available.
************************************************************************************/
module.exports = function(app, pool, logger, express, jwt) {
  var helper = require("../modules/helpers")(logger)
  var router = express.Router()

  // Get all states (ids and names)
  router.get('/list', function(req, res) {
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT * FROM states", function(err, sts) {
        if (err) return helper.handleErr(err, res)

        res.json(sts)
      })
    })
  })

  //Add a new state for a version. When done, pass back the updated version with it's states.
  router.post('/add/:ver_id', function(req, res) {
    var vals = req.body
    if (!('state' in vals) || !('launch' in vals)) helper.createErr("A state and launch date are required.", res)
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT * FROM versions WHERE id="+req.params.ver_id, function(err, ver) {
        if (err) return helper.handleErr(err, res)
        if (!ver.length) {
          //No result was found.
          return helper.createErr("A version with id +"+req.params.ver_id+" does not exist.", res)
        }
        conn.query("SELECT * FROM state_transition WHERE version_id="+ver[0].id+" AND state="+vals.state+" AND launch_date='"+vals.launch+"'", function(err, st) {
          if (err) return helper.handleErr(err, res)
          if (st.length) return helper.createErr("The version "+ver[0].version+" already has an entry for state "+vals.state, res)
        })
        console.log("INSERT INTO state_transition(version_id,state,launch_date) VALUES("+ver[0].id+", "+vals.state+", '"+vals.launch+"')")
        conn.query("INSERT INTO state_transition(version_id,state,launch_date) VALUES("+ver[0].id+", "+vals.state+", '"+vals.launch+"')", function(err, out) {
          if (err) return helper.handleErr(err, res)
          logger.info("New state added for version "+ver[0].version)
          res.json({"status":"ok", "message":"State added."})
        })
      })
    })
  })

  //Update the launch date of a state for a version
  router.post('/update/:ver_id/:state', function(req, res) {
    var vals = req.body
    if (!('launch_date' in vals)) helper.createErr("A launch date is required.", res)
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT * FROM versions WHERE id="+req.params.ver_id, function(err, ver) {
        if (err) return helper.handleErr(err, res)
        if (!ver.length) {
          //No result was found.
          return helper.createErr("A version with id +"+req.params.ver_id+" does not exist.", res)
        }
        conn.query("SELECT * FROM state_transition WHERE version_id="+ver[0].id+" AND state="+req.params.state, function(err, st) {
          if (err) return helper.handleErr(err, res)
          if (!st.length) return helper.createErr("This state does not exist yet for this version.", res)
          conn.query("UPDATE state_transition SET launch_date='"+vals.launch_date+"' WHERE version_id="+ver[0].id+" AND state="+req.params.state, function(err, out) {
            if (err) return helper.handleErr(err, res)
            logger.info("Launch date updated.")
            res.json({"status":"ok", "message":"Launch date updated."})
          })
        })

      })
    })
  })

  // Get a state and it's name by the id.
  router.get('/:state', function(req, res) {
    pool.getConnection(function(err, conn) {
      if (err) return helper.handleErr(err, res)
      conn.query("SELECT * FROM states WHERE state_id="+req.params.state, function(err, sts) {
        if (err) return helper.handleErr(err, res)

        res.json(sts[0])
      })
    })
  })

  return router
}

【问题讨论】:

  • 仅供参考,我在app.use(CORS(corsOpts)) 这一行中没有看到corsOpts 的定义。另外,为什么要创建verRouterstageRouter 而不使用它们?
  • 你确定你没有做类似泄露数据库连接从而耗尽连接池的事情吗?您只披露了创建连接池的代码,而不是使用它的代码,因此我们无法从您披露的代码中分辨出来。
  • 你去哪儿了?我在你发布后不久提出了一个想法,现在你似乎已经走了!如果您不只是发布并消失一段时间,Stack Overflow 效果最好。它比其他网站更具互动性,您的主要机会是在发布后的前 30-60 分钟内让人们看到并回答您的问题。如果您在那段时间没有与人交流,您可能会错过很多寻求帮助的机会,特别是如果您的问题需要反复来回才能得到回答。
  • 抱歉,我在工作时间发帖,是在离开前不久,现在一直很忙。此外,由于某种原因,我没有收到电子邮件。所以我删掉了一些我在那里进行测试的东西,其中包括 corsOpts 变量。我实际上并没有使用它,只是用 cors() 默认初始化。我认为这是 CORS 问题的原因是因为我在没有 LDAP 授权的情况下设置了这一切,因此没有设置 CORS,这从未发生过。但是,一旦我添加了 CORS,这种情况就开始发生了。不过,如果他们有帮助,我可以发布我的其他路线。
  • 好吧,很多人不会再参与那些有 24 小时周转时间的问题。这种类型的网站效率太低了。以后,我建议您仅在您可以在接下来的一个小时内多次检查该站点时才发布。这就是该网站最适合发布问题的人和提供答案的人的方式。

标签: node.js express npm cors


【解决方案1】:

由于我的猜测被证明是问题所在,我将把它放在答案中,这样你就可以结束这个问题了。

您的症状听起来好像您没有关闭连接(将其返回到连接池),因此在几次请求之后,连接池为空,服务器只是坐在那里等待连接可用。

【讨论】:

    猜你喜欢
    • 2014-02-08
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 2016-04-16
    • 2014-05-01
    • 1970-01-01
    • 2019-08-26
    相关资源
    最近更新 更多