Node.js/Express.js请求调用机制及同端点请求阻塞问题咨询
嘿,作为从C#转Node.js的过来人,太懂你这种遇到和.NET不一样的请求处理逻辑时的困惑了!你的测试现象其实是浏览器的请求策略加上Node.js/Express的单线程事件循环共同作用的结果,咱们一步步拆解清楚:
为什么同端点请求排队,不同端点却能立即处理?
1. 浏览器的同URL请求排队机制
这是最关键的原因!大部分现代浏览器(Chrome、Firefox、Edge等)对相同URL的GET请求有个默认的“串行发送”规则:当第一个请求还处于pending状态时,后续的同URL GET请求会被暂时挂起,直到前一个请求完成(不管成功还是失败)。
这么做的目的很实在:
- 避免重复请求浪费带宽和服务器资源
- 方便复用HTTP缓存(如果第一个请求返回了可缓存的响应,后续请求直接拿缓存就行,不用再发请求)
所以你打开前3个localhost:3000标签页时,浏览器是一个接一个地发送请求,而不是同时发,这就导致你看到第2、3个请求必须等前面的结束才会被服务器处理。而localhost:3000/2是完全不同的URL,浏览器会把它当成独立请求,直接新开连接发送,自然不会排队。
2. Node.js/Express的请求处理模型
再说说Node.js这边的逻辑:Node.js是单线程事件循环模型——主线程负责处理JS逻辑和事件回调,但它的I/O操作(比如读写文件、数据库查询)是异步非阻塞的。
这里分两种情况看:
- 如果你的
/路由处理是异步非阻塞的(比如只是返回一个简单的res.send('hello'),或者包含异步I/O操作),那主线程处理完第一个请求的回调后,会立刻回到事件循环处理下一个请求——但因为浏览器把同URL请求排队了,所以你还是会看到串行处理的现象。 - 如果你的
/路由处理是同步阻塞的(比如写了一段耗时的同步计算,比如for (let i = 0; i < 1e9; i++) {}),那主线程会被这段代码完全卡住,这时候不管是同URL还是不同URL的请求,都会被阻塞,直到这段同步代码执行完。但你测试里/2的请求能立即处理,说明你的/路由处理应该是异步非阻塞的,排队的核心原因还是在浏览器那边。
快速验证这个结论的小技巧
你可以用这两个方法验证:
- 用Postman或者curl同时发送多个
localhost:3000的请求,你会发现这些请求会被Express同时处理(只要是异步非阻塞的处理),根本不会排队——因为Postman没有浏览器的同URL排队限制。 - 或者在浏览器里打开
localhost:3000和localhost:3000?foo=1(带不同查询参数的同路径),浏览器会把它们当成不同的URL,也会同时发送请求,不会排队。
顺便聊聊Express的请求处理流程
既然你在学Express,顺便把请求处理的完整流程说清楚,帮你理解:
- 当服务器收到请求,Node.js的HTTP模块会把请求封装成
req(请求对象)和res(响应对象),然后交给Express的路由中间件链。 - Express会从上到下依次匹配路由规则,找到对应的处理函数。
- 处理函数执行:
- 如果是异步操作(比如
fs.readFile、数据库查询),处理函数会把异步任务交给Node.js底层的libuv线程池,然后主线程立刻回到事件循环,继续处理下一个请求;当异步任务完成后,回调会被放到事件循环的队列里,等主线程空闲时执行,再返回响应。 - 如果是同步操作,主线程会一直执行到处理函数结束,期间完全没法处理其他请求,这就是所谓的“阻塞”。
- 如果是异步操作(比如
内容的提问来源于stack exchange,提问作者balexandre
相关产品推荐
相关产品推荐

