【问题标题】:Node.js server stops respondingNode.js 服务器停止响应
【发布时间】:2016-12-01 10:08:38
【问题描述】:

我正在处理 node.js HTTPS 请求。我已经做了足够的研究并检查了所有东西,但我仍然认为我遗漏了一些东西。 我想要实现的是,当有人向我的 URL 发送 HTTPS GET 请求时,我必须向他们发送一个文件作为响应,以便他们可以下载。一切正常,我已经实现了“FS”、“HTTPS”,并且它也以正确的方式写入,但是当我在每 1000 个请求后在我的 URL 上发送大量流量时,平均有 40 个请求失败。

我不知道为什么会这样,我也设置了这个:

var https = require('https');
https.globalAgent.maxSockets = Infinity;

谁能帮我理解和解决问题? 提前致谢!

Anvesh

编辑:根据要求,我正在添加我的代码,出于隐私考虑,我删除了几行代码。

var https = require('https');
https.globalAgent.maxSockets = Infinity;
//var port = process.env.port || 1337;
fileSystem = require('fs'),
fileToWriteLog = require('fs'),
    path = require('path');
var mime = require('mime');
var cors = require('cors');


var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

var express = require('express'),
    app = module.exports.app = express();

app.use(cors());


// Created to use HTTPS
var options = {
    pfx: fileSystem.readFileSync('Certificate/key.pfx'),
    passphrase: 'XXX'   
};

var server = https.createServer(options,app);
var io = require('socket.io').listen(server);  //pass a https.Server instance
var port = 8080;
console.log(process.env.PORT);
server.listen(port,"0.0.0.0", function () {
    console.log("Secure Express server listening on port " + port);
});  //listen on port 8082

// routing

app.get('/stream/:patam1/:param2', function (request, response) {    
        var patam1 = request.param("patam1");
        var param2 = decodeURIComponent(request.param("param2"));


                var filePath = "XXXX" + param2;
                console.log(filePath);

                var stat = fileSystem.statSync(filePath);
                response.writeHead(200, {
                    'Content-Type': 'XXX', 
                    'Content-Length': stat.size
                });

                var readStream = fileSystem.createReadStream(filePath);
                readStream.on('data', function (data) {                                 
                    var flushed = response.write(data);
                    // Pause the read stream when the write stream gets saturated
                    if (!flushed)
                        readStream.pause();
                });
                response.on('drain', function () {                                  
                    // Resume the read stream when the write stream gets hungry 
                    readStream.resume();
                });
                // readStream.on('end', function () {                        
                     // response.end();
                // });          

});

【问题讨论】:

  • 你如何发送这 1000 个请求?您的服务器如何处理它们?请给我们更多信息。
  • 我准备了一次发送 1000 个请求的 windows exe 程序,而我的服务器受到所有这 1000 个请求的巨大影响。但这就是我们想要的,我们想要一次处理数百万个请求。服务器比较简单,有 HTTPS、fs 模块。它服务于 HTTPS GET 请求。
  • 在您的 Windows 应用程序和您的服务器之间有几个队列,如果它们不能对 1000 个请求进行排队,它们可能无法正常运行。我们需要查看更多代码,以确定是您的应用程序还是您的测试方法存在缺陷。
  • @David:我已经添加了我的代码,请帮忙。

标签: javascript node.js https


【解决方案1】:

一般问题

我不知道您的服务器在什么操作系统上运行。如果是 Linux 或 Windows,请查看有关您可以采取哪些措施来增加可能的并发连接数的答案:

您在这里遇到的问题可能与您的客户端(如您在 cmets 中所写的那样在 Windows 上运行)或服务器(您未指定其操作系统)有关。此外,问题可能出在您的程序或操作系统上,甚至可能出在客户端和服务器之间的其他一些东西上,例如代理等。

您可以使用 Apache ab 命令来测试您的服务器,这样至少您可以消除发送请求的程序在这里出错的可能性。例如:

ab -n 10000 -c 1000 -k http://localhost:8080/

这将发送总共 10000 个请求,一次 1000 个。

有关如何使用ab 测试服务器的更多详细信息,请参阅this

阻塞操作

这是你的问题:

var stat = fileSystem.statSync(filePath);

当您在处理程序中使用阻塞(同步)调用时,您总是会遇到并发连接问题。 我不知道这是否是唯一的问题,但只有当您希望您的服务器能够很好地处理并发连接时,您才需要使用异步调用。使用stat 而不是statSync

同步功能适用于程序启动时的一次性作业,例如使用readFileSync 填充options,而不是在事件处理程序中运行。

有关如何以多种不同方式(使用 Express,不使用 Express 等)在没有任何同步调用的情况下从磁盘提供文件的信息,请参阅此答案。见:

您根本不需要使用stat(或statSync)。您不必设置 Content-length 标头,因为 Node 可以使用分块编码,您可以只流式传输数据。

我会改变这个:

app.get('/stream/:patam1/:param2', function (request, response) {    
        var patam1 = request.param("patam1");
        var param2 = decodeURIComponent(request.param("param2"));

                var filePath = "XXXX" + param2;
                console.log(filePath);

                var stat = fileSystem.statSync(filePath);
                response.writeHead(200, {
                    'Content-Type': 'XXX', 
                    'Content-Length': stat.size
                });

                var readStream = fileSystem.createReadStream(filePath);
                readStream.on('data', function (data) {                                 
                    var flushed = response.write(data);
                    // Pause the read stream when the write stream gets saturated
                    if (!flushed)
                        readStream.pause();
                });
                response.on('drain', function () {                                  
                    // Resume the read stream when the write stream gets hungry 
                    readStream.resume();
                });
                // readStream.on('end', function () {                        
                     // response.end();
                // });          

});

这样的:

app.get('/stream/:patam1/:param2', function (request, response) {    
        var patam1 = request.param("patam1");
        var param2 = decodeURIComponent(request.param("param2"));

                var filePath = "XXXX" + param2;
                console.log(filePath);

                response.set('Content-Type', 'XXX');

                var readStream = fileSystem.createReadStream(filePath);
                readStream.on('open', function () {
                    response.set('Content-Type', 'XXX');
                    readStream.pipe(response);
                });
                readStream.on('error', function () {
                    response.set('Content-Type', 'text/plain');
                    response.status(404).end('Not found');
                });    
});

请参阅我在 GitHub 上的 node-static-http-servers 项目,尤其是 express example。它或多或少地完成了您在这里所做的事情。 here 有更详细的解释。

路径遍历

我的另一个建议是保护您的服务器免受路径遍历攻击。所以当你设置文件名时:

var filePath = "XXXX" + param2;

最好使用path模块:

var filePath = path.join("XXXX", param2);

然后测试它是否不在您要服务的目录之外,例如:

if (filePath.indexOf('XXXX/') !== 0) {
    return response.status(403).end('Forbidden');
}

indexOf 中的 / 斜线很重要。

异步统计示例

而不是这个:

         var filePath = "XXXX" + param2;
            console.log(filePath);

            var stat = fileSystem.statSync(filePath);
            response.writeHead(200, {
                'Content-Type': 'XXX', 
                'Content-Length': stat.size
            });

            var readStream = fileSystem.createReadStream(filePath);
            readStream.on('data', function (data) {                                 
                var flushed = response.write(data);
                // Pause the read stream when the write stream gets saturated
                if (!flushed)
                    readStream.pause();
            });
            response.on('drain', function () {                                  
                // Resume the read stream when the write stream gets hungry 
                readStream.resume();
            });

你可以这样写:

        // check for path traversal as I described above:
        var filePath = path.join("XXXX", param2);
        if (filePath.indexOf('XXXX/') !== 0) {
            return response.status(403).end('Forbidden');
        }
        console.log(filePath);

        fileSystem.stat(filePath, function (err, stat) {

            if (err) {
                response.set('Content-Type', 'text/plain');
                return response.status(404).end('Not found');
            }

            var readStream = fileSystem.createReadStream(filePath);

            readStream.on('open', function () {
                response.set('Content-Type', 'XXX');
                response.set(Content-Length': stat.size);
                readStream.pipe(response);
            });
            readStream.on('error', function () {
                response.set('Content-Type', 'text/plain');
                response.status(404).end('Not found');
            });

        });

【讨论】:

  • 我的 node.js 服务器在 windows 操作系统上运行。因此,根据您的建议,“stat”会做什么?它会解决并发连接的问题吗?感谢您的帮助!
  • @Anvesh 你不需要设置内容长度,所以你根本不需要使用sync。查看我更新的答案,我添加了一些代码示例。
  • 不,我需要在其他应用程序上,所以我必须传递文件的大小。
  • @Anvesh 如果您需要内容长度,那么您可以使用 stat 的异步版本。请参阅我对答案的最后更新。希望对您有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
相关资源
最近更新 更多