node.js - router.get 发送 res 两次
问题描述
所以我用我的脚本缩小了问题的范围。
问题是这条线
clients.push(res);
它似乎在做的是 2 / 3 次而不是 1 次。
在 console.log 它显示:
STATION_ID Completed
754542
get data connected!
undefined
STATION_ID Stream END
get POdcast ran
STATION_ID Completed
754542
get data connected!
undefined
STATION_ID Stream END
get POdcast ran
但是,当我从中删除 res 时,clients.push(res);
它会正常触发,但不会在浏览器中返回给客户端。
有什么建议么?
完整代码:
router.get('/track/:url(*)', (req, res) =>{
var url = req.params.url.substr(0);
console.log('/track/'+url);
var length = 0;
var e = 0;
/* AD SYS */
var remote = "https://storage.googleapis.com/ad-system/testfolder/OUTOFAREA.mp3";
var adsys = needle.get(remote)
/* PODCAST */
var filesize = needle.get(url, function(error) {
if(error){
e = 505;
res.send("<html><head></head><body>NOPE</body></html>");
console.log(error)
//filesize.end();
res.end();
}
});
adsys.on('response', function(resB) {
console.log("STATION_ID Completed");
length = Number(resB.headers['content-length']);
// console.log(length);
});
filesize.on('response', function(resC) {
console.error("get data connected!");
console.log(resC.headers['content-length']);
a = Number(resC.headers['content-length']);
length = length+a;
});
res.set({
"Content-Type": "audio/mpeg",
'Transfer-Encoding': 'chunk',
// 'Content-Disposition': 'attachment',
// 'Content-Length':length
});
adsys.on("finish", function() {
console.log(" X STATION_ID Stream END");
getPodcast();
});
adsys.on("data", function (chunk) {
// console.log(clients.length);
/* if (clients.length > 0){
for (client in clients){
clients[client].write(chunk);
//console.log(chunk);
};
}*/
res.write(chunk);
});
function getPodcast(){
filesize.on("data", function (chunk) {
res.write(chunk);
});
filesize.on('end', function(){
console.log("end");
res.end();
});
}
});
module.exports = router;
解决方案
好的,现在您拥有更多完整代码,因此我可以看到您要完成的工作。
我看到的几个问题:
- 该变量
a
未定义,因此它是一个隐式全局变量。那很糟。在本地某处声明它。 - 在计算组合内容长度时,您的两个请求之间存在完整的竞争条件。您的代码假设
adsys
为您提供了response
第一个,但这并不保证会发生。 - 您计算该长度,但实际上并未使用它。你不能把它放在你的
res.set()
指令中,因为当它运行时你还没有完成计算。 - 似乎您至少缺少对 adsys 请求的错误处理。
- 在我看来,在 needle 文档中,needle 请求的完成事件是
"done"
,不是"finish"
。
这是一个经过显着清理的代码版本,其中包含一堆新的日志记录以帮助调试:
let trackCntr = 0;
function getLogger() {
let id = ++trackCntr;
return function(...args) {
args.unshift(`(${id}): `);
console.log(...args);
}
}
router.get('/track/:url(*)', (req, res) => {
const log = getLogger();
const url = req.params.url.substr(0);
log(`(${trackCntr}) /track/${url}`);
/* AD SYS */
const remote = "https://storage.googleapis.com/ad-system/testfolder/OUTOFAREA.mp3";
const adsys = needle.get(remote);
/* PODCAST */
const filesize = needle.get(url);
let responseCntr = 0;
let length = 0;
let errReportedAlready = false;
adsys.on("response", function(resB) {
log(`adsys.on('response'), content-length=${resB.headers['content-length']}`);
length += +resB.headers['content-length'];
++responseCntr;
checkResponseCntr();
});
adsys.on("err", sendErr);
adsys.on("timeout", sendErr);
filesize.on("response", function(resC) {
log(`filesize.on('response'), content-length=${resC.headers['content-length']}`);
length += +resC.headers['content-length'];
++responseCntr;
checkResponseCntr();
});
filesize.on("err", sendErr);
filesize.on("timeout", sendErr);
// this is called if either needle requests gets an error
function sendErr(err) {
log("sendErr", err);
if (!errReportedAlready) {
errReportedAlready = true;
if (res.headersSent) {
// just need to abort the response because headers have already been sent
res.end();
} else {
// send error status
res.sendStatus(500);
}
}
}
// code continues here after both response headers above have completed
function checkResponseCntr() {
log(`checkResponseCntr(${responseCntr})`)
// if we have received both responses, then start streaming ad data
if (responseCntr === 2) {
log("got both responses");
res.set({
"Content-Type": "audio/mpeg",
"Transfer-Encoding": "chunk",
"Content-Length": length
});
// start streaming ad data
getAd();
}
}
function getAd() {
log("getAd()");
// this will cause adsys data to start flowing
adsys.on("data", function(chunk) {
if (!errReportedAlready) {
res.write(chunk);
}
});
adsys.on("done", function() {
log("adsys done");
// now trigger getting the podcast data
getPodcast();
});
}
function getPodcast() {
log("getPodcast()");
filesize.on("data", function(chunk) {
if (!errReportedAlready) {
res.write(chunk);
}
});
filesize.on("done", function() {
log("filesize done");
if (!errReportedAlready) {
log("res.end()")
res.end();
}
});
}
});
module.exports = router;
这将执行以下操作:
- 正确计算长度而不考虑两个请求的竞争条件顺序。
needle()
为这两个请求添加错误处理。- 添加正确的
content-length
标题设置。 - 根据文档更改对完成的监视
needle()
以使用"done"
事件。needle()
- 代码
getAd()
和getPodcast()
类似的。
可能的问题仍然:
- 如果流式传输广告需要很长时间,我可以想象您的文件大小请求超时。
我可以在我自己的小 nodejs 应用程序中运行上面的代码,这是我得到的日志:
(1): (1) /track/https://storage.googleapis.com/radiomediapodcast/wellwellnow/season1/S01E04.mp3
(1): adsys.on('response'), content-length=754542
(1): checkResponseCntr(1)
(1): filesize.on('response'), content-length=63062853
(1): checkResponseCntr(2)
(1): got both responses
(1): getAd()
(1): adsys done
(1): getPodcast()
(2): (2) /track/https://storage.googleapis.com/radiomediapodcast/wellwellnow/season1/S01E04.mp3
(2): adsys.on('response'), content-length=754542
(2): checkResponseCntr(1)
(2): filesize.on('response'), content-length=63062853
(2): checkResponseCntr(2)
(2): got both responses
(2): getAd()
(2): adsys done
(2): getPodcast()
(2): filesize done
(2): res.end()
(1): filesize done
(1): res.end()
您可以清楚地看到两个单独的请求进入。第二个请求在第一个请求发送它的标头后立即到达 - 不知道为什么。
我已经确定双重请求是由将音频 URL 放入 Chrome URL 栏中引起的。如果我将该 URL 放入 HTML 页面中的音频标签中,那么我们将不再收到双重请求。我创建了这个简单的 HTML 页面:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<figure>
<figcaption>Listen to the T-Rex:</figcaption>
<audio
controls
src="http://localhost/track/https%3A%2F%2Fstorage.googleapis.com%2Fradiomediapodcast%2Fwellwellnow%2Fseason1%2FS01E04.mp3">
Your browser does not support the
<code>audio</code> element.
</audio>
</figure>
</body>
</html>
然后,我得到了这个日志(对我来说看起来很正确):
(1): (1) /track/https://storage.googleapis.com/radiomediapodcast/wellwellnow/season1/S01E04.mp3
(1): Accept: */*
(1): adsys.on('response'), content-length=754542
(1): checkResponseCntr(1)
(1): filesize.on('response'), content-length=63062853
(1): checkResponseCntr(2)
(1): got both responses
(1): getAd()
(1): adsys done
(1): getPodcast()
(1): filesize done
(1): res.end()
而且,双重请求似乎是 Chrome/Edge URL 栏的事情,因为当我将 URL 放入 Firefox URL 栏中时,它不会在 Firefox 中发生。
推荐阅读
- tensorflow - 如何从 ANN 中提取有影响的特征?
- c# - 如何在 Azure 中处理 API 和客户端之间的身份验证
- java - Java泛型的统一参数传递和类型安全
- image-processing - 需要使用圆霍夫变换检测图像中的最大圆
- r - 有没有办法从 coxme 模型中获得事件概率?
- swift - 为 ObservableObject ViewModels 编写单元测试并发布结果
- c++ - 使用 popen 定时运行程序
- sympy - 带有非符号的 Sympy 导数
- c# - 如何修复字符串不断返回null c#
- hibernate - Spring Boot + Hibernate + JPA = 使用存储库的命名 oracle 过程调用失败