请教一下, nodejs/express 是如何处理多个请求的

2022-11-16 10:55:35 +08:00
 coldmonkeybit

就是没有太理解这一点,虽然知道 IO 操作通过 async/await 异步执行的话不会阻塞主线程运行,但是如果有多个请求的话 node 是如何处理的呢,就是请求本身是否会被异步处理吗,还是说同一时间,只会有一个请求被处理,直到遇到 async/await ,如果有其他请求的话,主线程才会处理这些请求。
由于对并发问题不是特别了解,请各位大佬指教一下。

3484 次点击
所在节点    程序员
23 条回复
Maboroshii
2022-11-16 11:08:01 +08:00
单线程程序同一时刻只能执行一行代码,await 可以让出 cpu 去执行其他的代码,等 await 有结果了,并且抢到 cpu 了,就回来继续执行。这是我的理解。
coldmonkeybit
2022-11-16 11:15:26 +08:00
@Maboroshii 大概明白了,同一时刻只能执行一行代码,就意味着每次只能执行一个请求,当遇到 await 让出 cpu 之后,才能执行第二个请求,如果想要同时处理多个请求,应该就需要用子进程之类的处理方式吧。
star7th
2022-11-16 11:26:45 +08:00
我没有认真去钻研底层,大多情况下只是一个使用者。但根据我对 nodejs 使用经验,大概是:
在同一个进程里,请求确实是异步处理的。await 挂起一个后,cpu 会处理另一个。但由于很快,所以你可以理解为“接近是同时处理的” 。对不同的两个用户来讲,同时访问 node 接口并没有什么明显延迟感觉 。
如果你是追求非常高的”同时“,那可以多进程处理。比如 eggjs 就可以起多个 work 进程。
好像新出一种特性,可以更细粒度在单进程里模拟多进程,减少进程切换成本。但是没太细了解。总之,如果你要追求高度同步处理,就起多个进程就行。
tux
2022-11-16 11:27:05 +08:00
不用子进程,正是异步的强大的地方,子进程多了,开销就大,异步没这方面开销,一个一个处理,效率高
wangtian2020
2022-11-16 11:27:29 +08:00
不要阻塞,指的是不要使用
名字带 Sync 的方法,比如
fs.writeFileSync
fs.readFileSync
因为他们会直接卡死主线程,直到结果返回,假如读文件花 1 秒钟,那这一秒钟内任何其他请求都会等待
不阻塞用老的回调方法,或者新的 promise 方法都行 比如 fs.promises.writeFile

举个例子,你写一个请求,读取本地 txt 文件,假如要花一秒钟。如果用非同步 Sync 的方法,那就是 nodejs 告诉系统,你去读这个文件,读完了通知我,我来执行回调。同步就是一直死等,其他事啥也不做。
https://www.zhihu.com/question/62254462/answer/1611867539

同时处理多个请求,不要用阻塞方法就行了,跟子进程没关系。除非你说的请求是,一个循环 1000 万遍的方法,那样真会卡 1 秒钟。
ysc3839
2022-11-16 11:42:00 +08:00
你需要知道整个程序大致的逻辑是这样的:
while (true) {
const 事件 = 等待事件();
if (事件) 事件.处理函数();
}
当有请求来了,就会调用处理请求的函数,直到函数返回,才会等待并处理下一个事件。

假如你的函数中 await 了别的异步事件,比如这样:
async function() {
await sleep(10);
send('ok');
}
可以把 async function 看成回调函数的语法糖,实际的逻辑类似:
function() {
sleep(10, function() {
send('ok');
});
}
await 时当前函数就返回了,继续等待事件,此时就可以处理其他请求了。sleep 完成后也会有个 sleep 完成的事件,会调用传递给 sleep 的回调函数,看上去就是 await 完成了继续执行。
no13bus
2022-11-16 11:59:57 +08:00
@star7th 协程?
star7th
2022-11-16 12:06:18 +08:00
himself65
2022-11-16 12:16:50 +08:00
作为 nodejs member 补充一下个人看法,nodejs 底层是包装了 libuv ,对于用户(开发者)来说是单线程,但是底层可能会给不同的线程处理你的异步请求(比如读文件)

比如随时找了个文章: https://www.digitalocean.com/community/tutorials/how-to-use-multithreading-in-node-js#
otakustay
2022-11-16 12:39:45 +08:00
不用 cluster 的话,请求里的代码逻辑依然是单线程的,即一个请求在 CPU 处理(非 IO )时,另一个请求要 CPU 是会卡住的
okakuyang
2022-11-16 13:01:14 +08:00
请求可以接收很多,但是只能一个个处理,因为 node 是单线程。也可以用 node 的多线程,这样看起来就像有多个 node 分别处理请求。
dcsuibian
2022-11-16 13:06:28 +08:00
就是单线程的,每个时刻只能处理一个请求。(不包括 worker )

但关键在于,CPU 的速度远远快于 IO ,时间往往浪费在 IO 等待而不是程序运行上。
按我的理解,Nodejs 就是把这种 IO 操作交给了底层,底层可以是多线程的,总之你别管。

所以如果是 IO 密集型应用,那么 Nodejs 是非常合适的,但对于计算密集型应用,就确实会卡住了。
ochatokori
2022-11-16 13:21:15 +08:00
请求对 node 来说也是异步的 io
cctv1005s927
2022-11-16 16:56:34 +08:00
Linux 上是 epoll 啦: https://kaleid-liner.github.io/blog/2019/06/02/epoll-web-server.html

再进入 internal function 之前的所有 JS 代码都在单线程上跑着,然后进入 internal function 之后就把 http 请求交给 epoll 去处理了。
dudubaba
2022-11-16 17:09:20 +08:00
请求是异步,执行是同步。假如 1000 个请求一起进来,然后代码里给个超时任务,那所有的请求都会被挂起。所以 node 里一般不做同步操作磁盘或者计算等操作。
Jamy
2022-11-16 17:12:47 +08:00
执行 js 代码的线程是只有一个,所有需要 js 处理的操作都会进入一个队列,node 按照特定的规则处理队列数据,
当涉及到 io 时会使用操作系统提供的 io 复用接口或者新开线程特殊处理,处理完成再把结果扔回队列
nielinjie
2022-11-16 17:19:05 +08:00
楼主要搞清楚的是单线程 /多线程、同步 /异步、阻塞 /非阻塞这几个概念。这几个有关系但不相同。
Yeen
2022-11-16 17:28:03 +08:00
首先要清楚,nodejs 也是基于 js 语言模式,因此主线程也是 event loop 的。
网上讲这个资料的很多可搜一下。

每次由事件触发一次处理,进入 /退出事件处理代码(就是用户代码)。

async/await 无非就是某次进入 event loop 的代码执行显式的停留在等待 promise 的 resovle/reject 的调用点,直到 promise 已决才继续往下执行,注意只是代码执行停留,但线程不阻塞。此时主线程完全可能进入其他的 event loop 处理,(比如其他地方定时器唤醒),因此是异步的。
coolloves
2022-11-16 17:37:30 +08:00
cy 慢慢看
KouShuiYu
2022-11-16 21:16:18 +08:00
我的理解只要不是调用了同步方法各种 xxxSync 或者 CPU 在一直计算比如从 1 循环加到一亿,都可以

你可以试试
先访问 http://127.0.0.1:3000/?t=10000 再访问 http://127.0.0.1:3000/?t=0 第一次结果还没返回,第二次结果是秒回的

const http = require('http');
const { URL } = require('url');
const { setTimeout } = require('timers/promises');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer(async (req, res) => {
const t = new URL(`http://${hostname}:${port}${req.url}`).searchParams.get('t');
// 模拟耗时操作
await setTimeout(t);

res.end(`Hello World:${t}`);
});

server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}`);
});

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/895617

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX