使用fetch检测在线音频流状态返回200时卡住的解决方案咨询
问题原因分析
你遇到的fetch卡住问题,本质是因为在线音频流是持续传输的无限长响应,node-fetch默认会等待完整响应体接收完成才会resolve Promise,服务器会一直推送流数据,所以await永远不会返回。而且你要的只是校验流是否可用,不需要接收完整的流内容,所以不需要等待响应体加载。
另外你原有代码存在逻辑问题:第一个链接校验失败后直接return,不会遍历后续备用的流地址,下面的修复方案也同步解决了这个问题。
解决方案
方案1:仅校验响应头后立即终止请求
你不需要等待响应体返回,只要拿到200的响应头就可以判定流可用,拿到响应后直接销毁连接即可,修改后的代码如下:
export async function startBroadcast(this: Root) { if (!this.extensions.broadcast) { this.extensions['broadcast'] = this.client.voice?.createBroadcast() if (this.config.radio?.streamLink) { if (this.extensions.broadcast) { const broadcast = this.extensions.broadcast as VoiceBroadcast let tested: number = 0 for (const link of this.config.radio.streamLink) { let req: Response | undefined; const controller = new AbortController(); // 设置5秒超时兜底,避免网络异常卡住 const timeout = setTimeout(() => controller.abort(), 5000); try { req = await this.fetch(link, { signal: controller.signal }); } catch (e) { this.log(LoggingLevels.error, `${link} 请求超时或连接失败`); tested++ if (tested == this.config.radio.streamLink.length) { this.log( LoggingLevels.emergency, `All stream links failed. Radio is likely offline` ) throw new Error('All stream links failed') } // 校验失败继续试下一个链接 continue; } finally { clearTimeout(timeout); } // 拿到响应头后直接销毁响应体,不等待音频内容传输 if (req.body) req.body.destroy(); if (!req.ok) { this.log(LoggingLevels.error, `${link} 不可用,状态码:${req.status}`) tested++ if (tested == this.config.radio.streamLink.length) { this.log( LoggingLevels.emergency, `All stream links failed. Radio is likely offline` ) throw new Error('All stream links failed') } continue } const broadcastOptions: StreamOptions = { highWaterMark: 50, volume: false } this.log( LoggingLevels.debug, `Starting up broadcast using stream link: ${link}` ) broadcast.play(link, broadcastOptions) break } } } } }
方案2:全局适配不同版本node-fetch的超时逻辑
- 如果你用的是v2版本node-fetch,可以直接传
timeout参数代替AbortController:this.fetch(link, { timeout: 5000 }) - 如果你用的是v3及以上版本node-fetch,沿用上面代码的AbortController写法即可。
注意事项
- 不要调用
res.json()、res.text()、res.arrayBuffer()这类需要读取完整响应体的方法,否则还是会出现卡住问题 - 只要拿到响应头的状态码就足够判断流是否可用,不需要接收实际的音频内容
内容的提问来源于stack exchange,提问作者Jonathan Stevens
相关产品推荐
相关产品推荐

