Nodejs新人求助:http.response做长链接数据流时,每隔14分钟左右会被断开 【已解决】

Nodejs新人求助:http.response做长链接数据流时,每隔14分钟左右会被断开 【已解决】

本人在用nodejs实现一个流媒体服务器,http协议;通过管道从编码器获取媒体二进制数据后,以长链接无限的大文件的形式向client输出(直播)。发现每隔14分钟左右,链接会中断,通过打日志确认与发送的数据长度无关;排除socket timeout的问题。目前无头绪,有高手了解不?

11 回复

Node.js 新人求助:http.response 做长链接数据流时,每隔14分钟左右会被断开 【已解决】

问题描述

最近我在使用 Node.js 实现一个流媒体服务器,采用 HTTP 协议。具体来说,我通过管道从编码器获取媒体二进制数据,然后以长链接的方式将这些数据源源不断地输出给客户端(即进行直播)。然而,我发现每隔大约14分钟,连接就会被意外断开。通过打日志确认,这个问题与发送的数据长度无关,并且可以排除掉 socket 超时的问题。目前我对如何解决这个问题感到困惑,希望得到一些指导。

解决方案

经过一番排查,我发现问题出在了 HTTP 长连接的维持上。默认情况下,某些中间设备(如负载均衡器、代理服务器等)可能会在一段时间内没有接收到数据的情况下自动关闭连接。为了解决这个问题,我们需要确保在长连接中定期发送心跳包或空数据包来维持连接。

以下是一个简单的示例代码,展示了如何实现这一功能:

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
    if (req.url === '/stream') {
        res.writeHead(200, {
            'Content-Type': 'application/octet-stream',
            'Transfer-Encoding': 'chunked',
            'Connection': 'keep-alive'
        });

        const stream = fs.createReadStream('./large-file.bin'); // 假设 large-file.bin 是你的媒体文件
        stream.pipe(res);

        // 设置定时器,每10分钟发送一次空数据包,以保持连接
        setInterval(() => {
            if (!res.finished) {
                res.write('\n'); // 发送一个空行
            }
        }, 10 * 60 * 1000); // 每10分钟发送一次

        // 在响应结束时清除定时器
        res.on('close', () => {
            clearInterval(timer);
        });
    }
});

server.listen(3000, () => {
    console.log('Server is running on port 3000');
});

解释

  • res.write('\n'): 定期发送一个空行,这样可以防止连接因长时间没有数据传输而被中间设备关闭。
  • setInterval: 使用定时器每10分钟发送一次空数据包。
  • res.on('close', ...): 当响应结束时(例如客户端断开连接),清除定时器,避免内存泄漏。

通过这种方式,我们可以有效地维持长连接,避免因中间设备的自动断开而导致的连接中断问题。希望这个解决方案对你有所帮助!


这个14分钟时基于日志记录得出。 这个场景其实很像一个超大的文件下载,网速不够时,下载时间较长,会在14钟左右中断一次?——有人遇到类似情况吗?

会不会跟什么内存缓存之类的东西有关?

stream 的输入输出有速度差,stream在输出不给力的时候要暂停一下。

不懂,是不是跟v8内存有关系,我看书看的云里雾里的。说Buffer,不是用的v8内存

自己顶一下。这个问题应该在所有http静态文件服务器实现上都会遇到啊(使用http.response),只要连续传输时间超过14分钟左右,就会收到response.event.close消息。——都没遇到??

keep-alive ? http trunk ?

我建议写一个能够重现问题的最小程序,托管在Github上。其他人clone下来,寻找问题的原因。

否则,大家都是猜测。效率不高。

浏览器会主动断开已经连接了很长时间的http请求的

问题已解决。 确认不关http.response的事。服务response输出的数据是由ChlidProcess.stdout获得的,问题出在子进程在stderr不断输出内容,而ChildProcess.spawn会默认的帮你打开stderr管道,你如果不读取stderr内容,一段时间后stdout管道也会塞住不输出。正确的做法是如果子进程特定管道输出内容不需要处理,需要用“Ignore”设置之。例如: require(‘child_process’).spawn(‘prg’, [], { stdio: [‘pipe’, ‘pipe’, ‘ignore’] });

相关手册好像没有说明这个情况,折腾了好久,真心悲剧。

在使用Node.js实现HTTP长连接传输大文件(如流媒体)时,如果遇到每隔一段时间(例如14分钟)连接被断开的问题,通常是因为HTTP请求或响应中的某些默认超时设置导致的。即使你已经排除了socket timeout问题,还有其他因素可能导致连接被断开,比如客户端或服务器的防火墙设置、网络设备的配置等。

为了解决这个问题,可以尝试以下方法:

  1. 增加HTTP服务器的keep-alive超时时间: Node.js的HTTP模块默认情况下会关闭非活跃的连接。你可以通过设置server.keepAliveTimeout来增加keep-alive超时时间。这可以通过在创建HTTP服务器时设置server.keepAliveTimeout属性来实现。

    const http = require('http');
    
    const server = http.createServer((req, res) => {
      if (req.url === '/stream') {
        // 设置响应头,表明这是一个持久连接
        res.writeHead(200, {'Content-Type': 'application/octet-stream', 
                             'Connection': 'keep-alive',
                             'Transfer-Encoding': 'chunked'});
        
        // 假设`streamSource`是一个可读流,表示从编码器获取的媒体数据
        req.pipe(streamSource).pipe(res);
      } else {
        res.end();
      }
    });
    
    // 增加keep-alive超时时间到30分钟
    server.keepAliveTimeout = 30 * 60 * 1000;
    
    server.listen(3000, () => console.log('Server running at http://localhost:3000/'));
    
  2. 确保防火墙或网络设备没有限制: 确保客户端和服务器之间的任何中间设备(如路由器、交换机或防火墙)都没有设置短的超时时间或定期关闭连接的策略。

  3. 检查客户端行为: 客户端也可能有自己的超时设置。如果可能,检查并调整客户端的配置。

以上步骤应该能帮助你解决问题。如果问题依然存在,可能需要进一步检查网络环境和客户端的具体配置。

回到顶部