Nodejs中同浏览器访问express的相同route会被阻塞是为什么?
Nodejs中同浏览器访问express的相同route会被阻塞是为什么?
问题是这样的, 本来是想做一个提供抓取服务的 API 项目, 使用的是 express. 在测试的过程中, 测试一下异步抓取的效果, 结果在同一个浏览器的多个 Tab 访问同一个 route 会被阻塞住, 然后在 stackoverflow 中找到这篇一样的问题, 最后一句话 “I got the reason, it is because I ran two ‘/a’ on the same browser. I just tried to run one in chromium, and the other one in firefox, they were handled asyncly. Looks interesting.” 然后我在本地用 ruby 脚本写了个多线程访问测试了一下, 又能够异步并不阻塞(用不同的浏览器也可以).
在 Node.js 入门 中也有一个编写非阻塞的例子, 其使用的是 node.js 的 http 库, 并没有像 express 那样同浏览器访问会阻塞的情况, 所以我猜想应该是 express 在处理请求的时候做了某些处理(猜测在 Cookie 上), 让同浏览器的访问进行了阻塞. 因为刚刚接触 express 不久, 又想弄清楚是怎么一回事, 是否有人知道是 express 做了什么处理或者是什么地方的影响?
Node.js 中同浏览器访问 Express 的相同 route 会被阻塞的原因
问题背景
在开发一个提供抓取服务的 API 项目时,使用了 Express 框架。在测试过程中发现,在同一个浏览器的多个标签页访问同一个路由(route)时,请求会被阻塞。而在不同浏览器或通过多线程脚本访问时,则不会出现这种情况。
原因分析
这个问题的核心在于浏览器的行为和 Express 框架如何处理并发请求。具体来说:
-
浏览器行为:
- 当同一个浏览器的不同标签页(Tab)向同一个服务器发起请求时,这些请求可能会被浏览器进行一定的管理。
- 浏览器通常会对来自同一站点的请求进行排队,以避免过多的并发请求导致服务器负载过高。
-
Express 处理机制:
- Express 是基于 Node.js 构建的,Node.js 默认是非阻塞、事件驱动的架构。
- 但是,当 Express 接收到请求时,它会根据请求的具体情况来决定如何处理这些请求。例如,如果请求中包含某些特定的头部信息(如
Cookie
),可能会导致 Express 对这些请求进行顺序处理。
示例代码
为了更好地理解这一现象,我们可以通过以下简单的示例代码来模拟这种场景:
const express = require('express');
const app = express();
const port = 3000;
app.get('/test', (req, res) => {
console.log(`Handling request: ${new Date().toISOString()}`);
setTimeout(() => {
res.send(`Response at ${new Date().toISOString()}`);
}, 5000); // 模拟长时间处理
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`);
});
在这个示例中,当我们访问 /test
路由时,服务器会模拟一个耗时 5 秒的处理过程。如果我们同时在同一个浏览器的多个标签页中访问该路由,请求可能会被阻塞,直到上一个请求完成。
解决方案
要解决这个问题,可以尝试以下几种方法:
- 使用不同的浏览器或无痕模式:这样可以避免浏览器对同一站点的请求进行排队。
- 设置不同的
Cookie
或其他头部信息:这可能会改变请求的处理方式,从而避免阻塞。 - 修改 Express 的中间件配置:例如,可以添加自定义的中间件来处理并发请求。
通过以上分析和示例代码,我们可以更好地理解为什么在同一个浏览器中访问相同的路由会导致请求被阻塞,并且知道如何解决这个问题。
你的express页面执行时间是多少呢?
尝试关闭多个TAB测试,而在单一页面上用F5刷新测试,响应时间是否与单次访问的时间差不多? 如果是,那就是浏览器的原因,如果不是,要阻塞页面只能是这个抓取是同步操作,或者你队列了它。express处理URL只是调用一系列函数,不可能做这种阻塞的
老外也说了,换成2个浏览器就正常,证明服务端是没问题的,问题出在浏览器
你这么一说, 我拿 Chrome 与 Safari 做了下测试, 每个浏览器同时打开两个 Tab 访问 localhost:3000/a :
express = require 'express'
app = express()
app.use(express.logger('dev'))
app.get ‘/a’, (req, res, next) ->
f = ->
res.send ‘a’
console.log ‘end’, new Date()
console.log ‘sleep’, new Date()
setTimeout f, 10000
app.get ‘/b’, (req, res, next) ->
res.send ‘b’
Safari 输出
GET /a 200 10001ms - 1
end Mon Dec 17 2012 13:27:15 GMT+0800 (PHT)
sleep Mon Dec 17 2012 13:27:15 GMT+0800 (PHT)
GET /a 200 10000ms - 1
end Mon Dec 17 2012 13:27:16 GMT+0800 (PHT)
sleep Mon Dec 17 2012 13:27:16 GMT+0800 (PHT)
Chrome 输出
GET /a 200 10003ms - 1
end Mon Dec 17 2012 13:29:39 GMT+0800 (PHT)
sleep Mon Dec 17 2012 13:29:39 GMT+0800 (PHT)
GET /favicon.ico 404 1ms
GET /a 200 10001ms - 1
end Mon Dec 17 2012 13:29:49 GMT+0800 (PHT)
sleep Mon Dec 17 2012 13:29:49 GMT+0800 (PHT)
GET /favicon.ico 404 1ms
这样看来, 是 Chrome 对没有返回的请求阻塞了下一个相同请求, express 没有做处理.
很意外… 很意外…
补充一个链接. Chrome 与 Firefox 都会对相同请求的 URL 进行串行化. http://www.laruence.com/2011/07/16/2123.html
在Node.js中,当使用Express框架时,同一个浏览器的多个Tab同时访问相同的路由不会被阻塞。根据你的描述,问题可能与浏览器的行为有关,而不是Express本身。
在同一浏览器的不同标签页中访问相同的URL时,浏览器可能会缓存请求结果,从而导致请求看起来像是被阻塞了。你可以尝试清除浏览器缓存或使用不同的浏览器来验证这一点。
示例代码
假设我们有一个简单的Express应用,该应用响应GET请求并返回当前时间:
const express = require('express');
const app = express();
app.get('/test', (req, res) => {
const now = new Date().toISOString();
console.log(`Handling request at ${now}`);
res.send(now);
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
解释
-
清除缓存:在浏览器中打开开发者工具(通常按F12),然后进入Network标签,勾选“Disable cache”。
-
使用不同浏览器:你可以尝试在不同的浏览器中打开同一URL,例如在Chrome和Firefox中分别打开
http://localhost:3000/test
。 -
多线程测试:你可以使用Python或其他脚本语言编写一个多线程测试程序,从不同的线程向服务器发送请求。这将帮助你验证是否是浏览器行为导致了请求阻塞。
如果上述方法仍然无法解决问题,可以检查是否有中间件或配置对请求进行了一些额外处理。但通常情况下,浏览器的缓存机制可能是导致问题的原因。