nodejs处理http请求是串行的吗
发布于 5 年前 作者 lichaosuper 5603 次浏览 来自 问答

nodejs在处理http或者https请求的时候,是把和该请求有关的异步任务都执行完,才能处理下一个请求吗,这点挺困惑的,下面是我的代码实践。

在node里面模拟一个异步任务

const http = require('http');
const url = require('url');
http.createServer(function(req, res) {
	// 革除/favicon.ico额外请求
	if(url.parse(req.url).path == '/favicon.ico') {
		return;
	}
	// 下面才是实践的主要内容
	console.log("开始处理新请求了");
	// 模拟5秒的长时间的异步任务
	setTimeout(function() {
		console.log('执行完异步任务了');
		res.end('ok');
	}, 5000)
}).listen(8080)

前端模拟在极短时间内有三个请求先后访问node服务器

for(var i = 0; i < 3; i++) {
		window.open("http://localhost:8080/", "_blank");
}

下面是实际运行结果 捕获.PNG

结果显示node在处理请求的时候会把和该请求有关的异步代码执行完毕,才会处理下一个请求,不知道我的理解正不正确,请大家赐教,谢谢。

12 回复

@zijin-m 捕获.PNG 用window.open(“http://localhost:8080/”+ i, “blank”)测试时,发现是我之前的理解错了,并不是要把异步代码执行完毕,才会处理下一个请求,感谢解答。 再追问一下,如果node主线程正在执行和上一个http请求有关的同步代码,如果这时又来了一个新的http请求,这时node主线程是继续执行之前的同步代码还是先去处理新的http请求呢?

@lichaosuper 你用不同浏览器请求,或者 用postman 就能看到真实情况了

你这个测试工具有问题,用ab

同一浏览器发起多次请求,还是会被当做同一个用户,阻塞在那。 你用不同浏览器,或者不同电脑,就能看到这块非阻塞了。

关于测试 模拟并发请求可以使用postman等工具,for循环中依旧是顺序请求

关于服务端代码如何运行 涉及知识点:nodejs的event loop如何工作 程序员对于同步执行的代码会比较容易理解,因为这符合人的思维习惯,可是像nodejs这种异步的代码,就有点看不懂它具体是怎么执行的了。但咱们会遇到这样的问题,别人也会遇到,其中一个人就做了一个“模拟器”,很直观的展示nodejs异步代码执行的整个过程。为了让问题中的代码能在模拟器中执行,我稍微修改了一下代码(本质不变,不影响理解):

//每次请求到达8080端口,就会开始执行httpRequest这个function(也就是问题中createServer中的callback) function httpRequest(req, res){ console.log(‘start to process request.’); setTimeout(function(){ console.log(‘finish callback’); //res.end(‘ok’); }, 7000); //为了更加直观,适当调整一下时间 console.log(‘ready to accept another request.’); } //模拟并发请求(其实不是并发,但是不影响理解,真正的并发也是同样的道理) httpRequest({},{}); httpRequest({},{}); httpRequest({},{});

来,我们先感性的看下这段代码的执行过程 因为压缩的比较严重,如果看不清,可以访问最后参考资料中的模拟工具链接,自行运行。如果觉得程序运行过快,可以点击左上角的图标,然后调整’Detlay time’来控制程序执行的速度。 nodejs_event_loop (1).gif

介绍一下该工具 Screen Shot 2019-08-26 at 3.11.57 PM.png

  • 左上角点击图标显示额外的设置界面。'delay time’用来调整代码执行的速度,'Simulate renders’用来模拟浏览器中的event loop和页面render的关系(nodejs的event loop和浏览器中的会有一些差异)
  • 中间是代码部分。
  • 右边’Call Stack’是栈,也就是楼主理解的运行同步代码的地方。 'Web Apis’表示所有异步的请求,比如http请求,IO等等,它们完成需要一些时间。 'Callback Queue’是一个存放各种callback的队列,比如web api中的某一个http请求完成了,那么对应的callback方法就会进到该队列。

相信到现在为止,大家应该对该代码的执行过程有所了解,其中有几个需要注意的点: 1.stack中的代码没执行完,Callback queue是不会出栈的。 2.截图中的’Callback queue’看起来就一个队列,其实真正的实现是多个队列,每个队列存放不同类型的callback,event loop基于某种顺序以及规则轮询这些队列,如果了解清楚这个,那么对于某些程序执行的结果就不会感到吃惊。

扩展思考 1.如果代码中出现CPU密集的任务,比如编码解码或者大循环,对程序会有什么影响?Nodejs如何处理CPU密集型的计算(提示:分片或partition,C++ addon,child process,cluster)? 2.如果一段代码中同时包含setTimeout,setInternal,Setimmediat,Process.nextTick,那么他们对应的Callback执行顺序是怎样的?其中Process.nextTick的使用需要注意什么?

参考资料 菲利普·罗伯茨:到底什么是Event Loop呢? nodejs event loop运行模拟工具 nodejs event loop 文档

@wxlfight 学习了,谢谢

@wxlfight 我使用nodejs event loop运行模拟工具执行了您的代码,发现node主线程在处理并发请求时,是逐个处理请求,执行和请求有关的同步代码,遇到异步事件就挂起,继续往后执行同步代码,异步事件有了结果就放到事件循环队列中去,node主线程如果处理完一个请求的同步代码,发现还有请求没处理,就继续去处理请求,先不去处理事件循环队列中的任务,等到所有请求都处理完,主线程空闲了,才会去执行事件循环队列中的任务,这也是node擅长I/O密集型,不擅长CPU密集型的原因,因为cpu密集型任务的话导致node主线程花费长时间在处理CPP密集型任务上,导致前一个请求(假设请求的返回是在异步任务里面)在事件循环的任务队列中没返回,而后一个请求又没空处理。不知这样理解正确不?

@lichaosuper 请教下 按照您的说法 那如果我在路由处理时使用async await将异步操作(例如数据库操作等)同步的执行下去,框架的路由处理都可以携程async函数嘛,那么是否前后两个请求是串行处理的呢?还是说因为路由处理函数时async了,两个请求就不会串行处理,是异步处理的?学习node一直很疑惑的一个点,大致明白像数据库查询等操作都是异步的,不会阻塞后面代码的执行,但是我们一般写代码时都用await等待查询结果了,不清楚请求处理函数到底是一个个执行,还是异步执行的。。。

回到顶部