在顺序HTTP请求阻止操作在节点?

请注意,对我的问题不相关的信息将被“引用”

像这样(随意跳过这些)。

问题

我正在使用节点代表多个客户端进行按顺序的HTTP请求。 这样,最初拿客户端几个不同的页面加载得到所需的结果,现在只通过我的服务器采取一个单一的请求。 我目前正在使用“asynchronous”模块进行stream量控制和“请求”模块进行HTTP请求。 大约有5个callback,使用console.time,从开始到结束需要大约2秒(草图代码如下)。

现在我对节点不熟悉,但是我知道节点的单线程本质。 虽然我已经多次阅读了这个节点,并不是为CPU绑定的任务而构build的,但是我直到现在才真正理解这个意思。 如果我对所发生的事情有正确的理解,这就意味着我目前所拥有的(在开发中)决不会超过10个客户。

因为我不是节点的专家, 所以我问这个问题(在标题中)来得到确认,使得几个连续的HTTP请求确实阻塞。

结语

如果是这样的话,我希望我会问一个不同的SO问题(在做适当的研究之后)讨论各种可能的解决scheme,如果我select继续在节点中处理这个问题(这本身可能不适合我正在尝试的去做)。

其他闭幕的想法

如果这个问题不够详细,太不清白,或者特别花言巧语(我尽量简洁),我真的很抱歉。

感谢和所有的人都可以帮助我的问题!

我之前提到的代码:

var async = require('async'); var request = require('request'); ... async.waterfall([ function(cb) { console.time('1'); request(someUrl1, function(err, res, body) { // load and parse the given web page. // make a callback with data parsed from the web page }); }, function(someParameters, cb) { console.timeEnd('1'); console.time('2'); request({url: someUrl2, method: 'POST', form: {/* data */}}, function(err, res, body) { // more computation // make a callback with a session cookie given by the visited url }); }, function(jar, cb) { console.timeEnd('2'); console.time('3'); request({url: someUrl3, method: 'GET', jar: jar /* cookie from the previous callback */}, function(err, res, body) { // do more parsing + computation // make another callback with the results }); }, function(moreParameters, cb) { console.timeEnd('3'); console.time('4'); request({url: someUrl4, method: 'POST', jar: jar, form : {/*data*/}}, function(err, res, body) { // make final callback after some more computation. //This part takes about ~1s to complete }); } ], function (err, result) { console.timeEnd('4'); // res.status(200).send(); }); 

通常情况下,node.js中的I / O是非阻塞的。 您可以通过同时向您的服务器发出几个请求来进行testing。 例如,如果每个请求需要1秒来处理,则阻塞服务器将需要2秒来处理2个同时请求,但是非阻塞服务器只需要多于1秒来处理这两个请求。

但是,您可以通过使用sync-request模块而不是请求来故意阻止请求 。 显然,这不是build议服务器。

下面是一些代码来演示阻塞和非阻塞I / O之间的区别:

 var req = require('request'); var sync = require('sync-request'); // Load example.com N times (yes, it's a real website): var N = 10; console.log('BLOCKING test =========='); var start = new Date().valueOf(); for (var i=0;i<N;i++) { var res = sync('GET','http://www.example.com') console.log('Downloaded ' + res.getBody().length + ' bytes'); } var end = new Date().valueOf(); console.log('Total time: ' + (end-start) + 'ms'); console.log('NON-BLOCKING test ======'); var loaded = 0; var start = new Date().valueOf(); for (var i=0;i<N;i++) { req('http://www.example.com',function( err, response, body ) { loaded++; console.log('Downloaded ' + body.length + ' bytes'); if (loaded == N) { var end = new Date().valueOf(); console.log('Total time: ' + (end-start) + 'ms'); } }) } 

运行上面的代码,您会看到非阻塞testing的处理时间大致与处理单个请求的时间相同(例如,如果设置N = 10,则非阻塞代码将执行10次快于阻止代码)。 这清楚地表明请求是非阻塞的。


附加答案:

你也提到你担心你的进程是CPU密集型的。 但是在你的代码中,你不是基准CPU实用程序。 您正在混合networking请求时间(I / O,我们知道它是非阻塞的)和CPU处理时间。 要测量请求在阻塞模式下的时间,请将您的代码更改为:

 async.waterfall([ function(cb) { request(someUrl1, function(err, res, body) { console.time('1'); // load and parse the given web page. console.timeEnd('1'); // make a callback with data parsed from the web page }); }, function(someParameters, cb) { request({url: someUrl2, method: 'POST', form: {/* data */}}, function(err, res, body) { console.time('2'); // more computation console.timeEnd('2'); // make a callback with a session cookie given by the visited url }); }, function(jar, cb) { request({url: someUrl3, method: 'GET', jar: jar /* cookie from the previous callback */}, function(err, res, body) { console.time('3'); // do more parsing + computation console.timeEnd('3'); // make another callback with the results }); }, function(moreParameters, cb) { request({url: someUrl4, method: 'POST', jar: jar, form : {/*data*/}}, function(err, res, body) { console.time('4'); // some more computation. console.timeEnd('4'); // make final callback }); } ], function (err, result) { res.status(200).send(); }); 

您的代码只在“更多计算”部分中阻塞。 所以你可以完全忽略等待其他部分执行的时间。 事实上,这正是节点可以同时处理多个请求的方式。 在等待其他部分调用相应的callback函数(您提到可能需要1秒钟)时,节点可以执行其他JavaScript代码并处理其他请求。

你的代码是非阻塞的,因为它使用了request()函数的非阻塞I / O。 这意味着在获取一系列http请求时,node.js可以自由地处理其他请求。

async.waterfall()是为了让你的请求顺序执行,并把结果传给下一个。 请求本身是非阻塞的, async.waterfall()不会改变或影响。 你只是意味着你连续有多个非阻塞请求。

你有什么类似于一系列嵌套的setTimeout()调用。 例如,这段代码需要5秒钟才能到达内部callback(就像你的async.waterfall()需要n秒才能到达最后一个callback):

 setTimeout(function() { setTimeout(function() { setTimeout(function() { setTimeout(function() { setTimeout(function() { // it takes 5 seconds to get here }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); 

但是,这基本上使用零CPU,因为它只是5个连续的asynchronous操作。 实际的node.js进程涉及的时间可能不会超过1ms,以调度下一个setTimeout() ,然后node.js进程可能会做很多其他的事情,直到系统发布一个事件来触发下一个计时器。

您可以阅读关于node.js事件队列如何在这些引用中工作的更多信息:

运行任意代码而等待在节点callback?

阻止非阻塞http服务器中的代码

隐藏在JavaScript / Node中的线程永远不会执行用户代码:是否有可能,如果有的话,可能会导致竞争条件的一个奥秘的可能性?

JavaScript如何在后台处理AJAX响应? (写关于浏览器,但概念是一样的)

如果我对所发生的事情有正确的理解,这就意味着我目前所拥有的(在开发中)决不会超过10个客户。

这不是一个正确的理解。 一个node.js进程可以同时容易地有数千个非阻塞的请求在运行。 您的顺序测量时间只是一个开始和结束时间 – 与CPU资源或其他操作系统资源消耗无关(请参阅下面有关非阻塞资源消耗的注释)。

然后,我仍然有关于使用节点来处理这个特定的应用程序 我担心它将如何扩展,因为它所做的工作不是简单的I / O,而是计算密集型的。 我觉得好像我应该切换到一个支持multithreading的平台。 我所问的/我所expression的关切是否有意义? 我可能只是在吐总BS,不知道我在说什么。

非阻塞I / O几乎不消耗CPU(当请求最初发送时只有一点点,当结果到达时稍微有一点),但是当compmuter等待删除结果时,根本不消耗CPU OS线程被占用。 这是node.js扩展非阻塞I / O的原因之一,因为当计算机正在等待删除站点的响应时,没有使用资源。

如果您的请求处理是计算密集型的(例如,需要处理大量纯粹阻塞的CPU时间),那么您可能需要探索在运行计算时涉及多个进程。 有多种方法可以做到这一点。 你可以用nodejs集群模块使用集群(所以你只需要有多个相同的node.js进程,每个进程处理来自不同客户端的请求)。 或者,您可以创build一个计算密集型工作的工作队列,并拥有一组执行计算密集型工作的subprocess。 或者,还有其他几个选项。 这不是需要从node.js中解决的问题types,可以使用node.js来解决。