Nodejs Can't set headers after they are sent,几个客户端并发访问必现
Nodejs Can’t set headers after they are sent,几个客户端并发访问必现
router.get(’/’, function (req, res, next) {
MongoClient.connect(url, function (err, db) {
if (err) {
console.log(‘connect error’);
throw err;
}
console.log(‘connect success’);
db.collection(‘live_collection’).find().toArray(function(err, result) {
if (err) {
console.log(‘find error’);
db.close();
throw err;
}
console.log(‘query success’);
db.close();
// console.log(result);
return res.json({message: ‘request for live success’, ret: 0, match: result});
})
})
});
return res.json({message: ‘request for live success’, ret: 0, match: result});这里出错,网上搜的说法和我这个都不一样,我这个用法很简单,应该不会存在 callback 被调用两次的情况。但据我抓包看,出错的时候服务会发送一个 404,这个应该是 express 自己发的,我的代码里面没有这样的逻辑。不知道为什么会有这样错误,折腾很久了也没找出原因,求大神帮助。。
每个分支都 return 一下。
请注意 return 是写在回调里的,这个请求到最后都没有回复任何内容。另外用 async await 来简化代码把,还有 db 不需要每个请求都去连接,可以在 node 启动时连接,然后设置 global.db = db。
router.get(’/’, async (req, res) => {
try {
await db.collection(‘live_collection’)
.find().toArray()
}
catch (e) {
}
res.json(…)
})
请求在 res.json 执行前已经被切断了
注意 MongoDB 如果写入操作失败不会 throw 错误,而是会返回一个 writeResult,需要自己 throw。
谢谢大佬帮助,但这个问题不是必现的,只有在并发多的时候才出现,感觉不像是你说的那样。我是个 node.js 小白,感觉 node.js 比较轻量用来写一个 web API 服务,但这个问题一直没搞定。。。
从 console.log 的输出情况来看,出错的时候 mongodb 的接口并没有出错。。
很有可能是并发的时候,建立了太多 db 实例,到一定上限的时候被 mongo 驳回了……
我的并发也不多,4~5 个客户端就出现了每个客户端 5 秒请求一次。。。实在不行,就换 python 或者 go 了
不需要每个请求都去 connect 一遍
这个错误“Can’t set headers after they are sent”在Node.js中很常见,通常是因为在同一个响应对象上多次调用了res.send()
、res.json()
、res.end()
等方法。这种情况在并发请求时尤其明显,因为多个请求可能会触发相同的逻辑错误。
以下是一个简单的示例,展示了如何错误地设置响应头,并导致这个错误:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
// 下面的代码会在第一次调用res.send()后抛出错误
res.send('Another response');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
要解决这个问题,你需要确保每个响应对象只被发送一次响应。这通常涉及到逻辑上的检查和条件分支,确保在发送响应后不会再次尝试发送。例如:
app.get('/', (req, res) => {
let responseSent = false;
someAsyncFunction((err, data) => {
if (!responseSent) {
responseSent = true;
res.send(data);
}
});
anotherAsyncFunction((err, moreData) => {
if (!responseSent) {
responseSent = true;
res.send(moreData);
}
});
});
在这个修改后的示例中,我们使用了一个responseSent
标志来确保响应只被发送一次。这是处理并发请求时避免重复发送响应的一种简单方法。