并发请求 - 为什么数据库连接会导致Node进程崩溃

时间:2014-11-17 05:00:38

标签: node.js node-mysql

概述

我正在使用NodeJS开发MVC应用程序。当应用程序第一次加载时,将创建数据库对象(使用池)

 
var pool = mysql.createPool({connectionLimit: 150, host: __host,
               user: __user, password: __password,
               database: __database})
module.exports = pool
 

收到请求后,将创建一个Controller对象,该对象将创建一个Model来执行操作。模型从池获取连接,执行操作,并将连接释放回池。

 
//router snippet
router.get('/post_data', function(req, res){
    router.setRequestAndResponse(req, res)
    var post_data = new Post_Data()
    post_data.processDataFromGet(router)
})

//controller code snippet
Post_Data_Controller.prototype.processDataFromGet = function(router){
    var controller_obj = this
    var data_array = {}
    var req = router.req, res = router.res
    //retrieving data from request and passing to the data_array
    controller_obj.model.create(data_array, function(result){
        var xml = xmlbuilder.create("response")
        if (result.code == "error"){
            xml.e("code", "error")
            xml.e("message", result.error_message)
        }else if (result.code == "success"){
            xml.e("code", "success")
        }
        controller_obj.sendResponse(router.res, xml, "xml")
    })
}

Post_Data_Controller.prototype.sendResponse = function(res, response, type){
    if (type == "json"){
        res.set({"Content-Type": "application/json", "Content-Length": JSON.stringify(response).length})
        res.send(response)
    }else{ /* Default type is XML */
        res.set({"Content-Type": "application/xml", "Content-Length": response.end({pretty: true}).length})
        res.send(response.end({pretty: true}))
    }
}

//Model snippet
Post_Data.prototype.create = function(data_array, callback){
    /* data validation */
    var fail = false, error_data = {}

    if (fail) {callback({code: "fail", cause: error_data}); return;}
//the next 2 lines do not throw an error when uncommented
    //callback({code: "fail", cause: "how's it going"});
    //return;
    __db_pool.getConnection(function(err, db_conn){
// the next two lines throw an error for two or more requests coming in at the same time
        callback({code: "fail", cause: "how's it going"});
        return;
        if (err) { callback({code: "error", error_message: err}); return;}
        callback({code: "fail", cause: "how's it going"});
        return;
          db_conn.query("sql command", [data_array],
            function(err, result){
                if (err){ callback({code: "error", error_message: err}); return;}
                if (result && result.length > 0){ //affiliate and listing exist
                    data_array.listing_id = result[0].listings_id
                    var data = [data_to_insert]
                    db_conn.query("sql command here", data,
                        function(err, result){
                            db_conn.release()
                            if (err){ callback({code: "error", error_message: err}); return;}
                            if (result && result.affectedRows > 0) {
                                callback({code: "success", data: {data_to_be_returned}})
                            }else {callback({code: "error", error_message:"Error inserting data"}); return}
                        })
                }else{
                    callback({code: "fail", cause: "error to send back"})}
            })
    })
}
 

问题

这些请求是Web服务请求。 如果我发送一个GET请求,则不会发生错误;但是,当我发送两个或更多并发请求时,我收到此错误:

 
/project_path/node_modules/mysql/lib/protocol/Parser.js:82
        throw err;
              ^
Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:689:11)
    at ServerResponse.res.set.res.header (/project_path/node_modules/express/lib/response.js:549:10)
 

我将罪魁祸首追溯到上面粘贴的模型代码中的特定行。似乎由于某种原因,一旦模型从池获得第二个请求的连接,它就会以某种方式干扰第一个请求。两个请求仍然将适当的数据插入数据库;但是,第二次和后续请求不能再发送响应而不再发送错误。

  我使用GET,POST和PUT内容类型执行了请求;只有GET才会抛出错误。所有其他内容类型都不会抛出任何错误,即使有超过一千个并发请求。

这是GET请求的Web服务代码;除了内容类型更改和正文中放置的数据之外,其他内容类型也是如此。

 
for(var i=0; i less than 5; i++){
    sendAsGet()
    i++
}

function sendAsGet(){
    try{
        var data = "?data_to_be_sent"
        var uri =url.parse("http://localhost:4000/post_data")
        var options = {hostname: uri.hostname, port: uri.port, method: "GET",
            path: uri.path + data, agent: false}
        request = (uri.protocol == "https")? https : http
        var req = request.request(options, function(res){
            var result = ""
            console.log("STATUS: " + res.statusCode)
            console.log("HEADERS: " + JSON.stringify(res.headers))
            res.setEncoding("utf8")
            res.setTimeout(50, null)
            res.on("data", function(chunk){
                result += chunk
            })
            res.on("end", function(){
                console.log(result)
            })
        })
        req.end()
    }catch(err){
        console.log(err.message)
    }
}
 

 我想知道两件事:

     
  • 为什么获取数据库连接会导致此问题?
  •  
  • 为什么只在GET请求上发生,而不是在POST和PUT上发生?
谷歌和以前的SO问题到目前为止还没有提供帮助。
感谢。

1 个答案:

答案 0 :(得分:1)

您看到错误的原因是您在路由器本身上放置了请求/响应实例。 不要这样做。路由器对象是一个"静态"对象,它不是每个请求的东西。所以目前这是正在发生的事情(按顺序):

  1. 请求#1进入并在req设置res / router并启动异步model.create()

  2. 同时,请求#2进入并覆盖req上的res / router并启动自己的异步model.create()

  3. 调用请求#1' model.create()回调,将响应发送到请求#2的套接字。

  4. 请求#2' model.create()回调被调用,它会尝试向刚刚刚刚回复的同一res发送回复。尝试将标题写入已发送的响应然后会导致您看到的错误。