node 单线程处理事件请求,一个请求卡住了,后续其他请求都会卡住,用 node 做业务处理,并发高的情况下,岂不是后面的请求可能会一直排队? node 不是单线程分发事件,多线程处理事件嘛?
1
kxuanobj 2022-01-20 17:21:48 +08:00
你不要卡住不就行了。。需要改改非要让代码卡住的问题。
|
2
BluesQu 2022-01-20 17:21:56 +08:00
node 不是单线程 JavaScript 才是单线程 一个请求卡住 暂时没有返回的话 并不影响后续请求的执行
|
3
Leviathann 2022-01-20 17:22:42 +08:00
卡住就下一个
|
4
anonymousar 2022-01-20 17:25:21 +08:00
@kxuanobj 楼主所说的卡住 应该是指 需要 cpu 计算的不可避免的那部分吧 类似 redis 中出现的慢查询。
不太了解 node 但是单线程利用多核可以走 multi process, 也可以搞 one event base per thread |
6
macy 2022-01-20 17:26:10 +08:00
node 实际上底层有个 libuv 线程池,看看 node 的时间循环就知道了,只要不是 cpu 密集型,把 cpu 卡住了,就没事。
|
7
Kasumi20 2022-01-20 17:27:08 +08:00
主线程处理连接,返回数据,数据的处理交给工作线程去干,一个连接对应一个线程可能比较耗费资源,所以要用协程之内的东西
|
8
Kasumi20 2022-01-20 17:33:37 +08:00 1
你可以看看我这个代码,对每个请求都会同步地调用 Youtube-dl ,但是不会卡住别的请求,因为有多线程呀
https://github.com/develon2015/Youtube-dl-REST/blob/node/index.js#L182 |
10
akira 2022-01-20 17:49:39 +08:00
CPU 计算密集型 放合适的地方啊, 为啥要指望一个语言打天下呢
|
11
for8ever 2022-01-20 18:09:17 +08:00
node 适合 IO 密集,不适合 CPU 密集
|
13
zonghow 2022-01-20 18:26:28 +08:00 via iPhone
一般都是根据 cpu 线程数 单机多实例部署吧
|
14
makelove 2022-01-20 18:40:49 +08:00
node 全异步怎么会卡住?业务很少有 CPU 密集部分,CPU 密集部分也不会放在 web 请求中,在 web 请求中搞 CPU 密集放哪个语言多进程框架都不行毕竟线程也是很有限的。
|
15
mxT52CRuqR6o5 2022-01-20 18:44:38 +08:00
[单线程分发事件,多线程处理事件] 多线程指的不是真正的系统层面上的线程,甚至你也可以把句话认为是错误的
node 那些常用的框架正常开发出来应该都是单核应用 正常的异步内部逻辑不会卡后续逻辑,如果你现在确实碰到了卡后续请求的问题,可能是使用了同步版本的 IO 调用(导致无法利用 JS 的异步能力),也可能是本身业务 cpu 运算量就大 简单点的利用多核的方法可以直接用 PM2 |
16
knives 2022-01-20 19:40:24 +08:00
担心主线程卡住,worker_threads 了解一下: http://nodejs.cn/api/worker_threads.html
|
17
hotsymbol 2022-01-20 23:36:08 +08:00
发给下游服务去处理。
|
18
3dwelcome 2022-01-20 23:46:22 +08:00
你想一下,领导给你一个人的任务太多,卡住怎么办?
很简单,把工作给下面的人分下去,人多力量大。 nodejs 也是同理,当然有些数据库之类的写入,还是尽量用消息队列处理。 |
19
ETiV 2022-01-20 23:55:52 +08:00 via iPhone
需要人灵活地使用工具
拿电钻去钉钉子是不对的 😂 |
20
LUO12826 2022-01-21 00:47:34 +08:00
@leebs 如果你这 1s 内 cpu (单核)占满,那确实后面的请求就得排队了。但一个请求要 cpu 实实在在算 1s 的业务,估计也不会用 node 写了。不过 node 也有了 worker_threads ,甚至可以调用 c++模块,一定程度上有利于计算密集型任务。
|
21
pengtdyd 2022-01-21 01:20:34 +08:00
你考虑的太多了,你应该思考的是你们公司有这么大的业务量吗
|
22
kuangwinnie 2022-01-21 02:03:42 +08:00
所以 node js 不适合用来做 CPU-intensive 的操作啊;本来就是拿来做 IO intensive 的。
|
24
georgema1982 2022-01-21 05:18:10 +08:00
一个请求卡住后,她会求助的:Step bro, I'm stuck
|
25
IvanLi127 2022-01-21 08:06:36 +08:00 via Android
你会卡住?那你就让这会卡住的部分放在这个线程外面,用其他线程跑就行了。
|
26
Biwood 2022-01-21 08:44:20 +08:00 via Android
event loop 怎么会卡住呢,甚至可以说处理高并发是优势,瓶颈在别的地方,几年前讨论的明明白白,现在这是退化了?
|
27
leebs OP @Biwood 业务事件是程序员写的,这是有可能会卡住的。如果某个请求因为某个条件触发了耗时的 cpu 运算,其他请求只能等待,多线程处理的情况下,其他线程是不需要等待的。
|
28
byte10 2022-01-21 09:35:47 +08:00
@leebs 我靠。。。你。。你。你绝对需要看我的视频啦少年,首先 nodejs 是 nio 模型,还有基于事件轮训的设计,它不用等待 IO ,所有的 IO 也是一个事件。一个请求过来,接收完请求后,就生成一个事件,这个事件处理完后,就扔出一个 response 事件,事件池就会一直被轮训,一直被处理。nodejs 主要适合 IO 密集型,就是不要那么有很多计算性的代码,也就是你的业务代码不要太复杂就行了。
NIO 有一个绝对性优势,你可以看看我的视频,有讲解 nodejs 如何无视 IO 时间,做到吞吐量保持一致。https://www.bilibili.com/video/BV1FS4y1o7QB |
29
yyfearth 2022-01-21 09:41:27 +08:00
@leebs node 单线程 如果做“CPU 密集的工作”或者“同步的 IO 操作”就会卡住
这个是一定要避免的 如果没法避免就必须用 worker https://nodejs.org/api/worker_threads.html 然后保证主线程不卡住就没问题了 所以 node 做 IO 密集的工作 性能还是可以的 不够就上多个 node 进程 然后外面做负载均衡就是 尽量无状态 如果必须要 session 就用 redis |
30
yyfearth 2022-01-21 09:48:06 +08:00
@leebs "某个条件触发了耗时的 cpu 运算"
耗时 CPU 运算目前两种方法,核心概念就是保证主线程不阻塞 1. 把这部分放到其他子线程或者进程 然后主线程异步处理 2. 如果上面太难 就把需要长时间 CPU 处理的运算打碎成多个短暂同步运算然后异步处理 保证主线程不要长时间卡住 对于 2 比如一个运算需要 10s 那么就把它打碎成 1000 个小运算 每个 0.1s 然后中间异步等待 这样主线程就不会因为一个 10s 的运算而阻塞 10s 虽热这样一来 原先 10s 的事情可能变成了 15s 或者更久 但是至少不会长时间阻塞其他的请求 当然 2 这种方法比较时候低并发或者低概率触发的情况 如果真的高并发情况大概率 还是要用 1 来处理 |
31
monkeyWie 2022-01-21 10:20:08 +08:00 1
我服了,CPU 密集和 IO 密集都不懂吗,在 JS 里一切 IO 都是异步的,不可能会卡,除非是 CPU 密集运算
|
32
juzisang 2022-01-21 10:44:25 +08:00
不知道现在前端 SSR 框架算不算 CPU 密集型计算,还有 node 里运行 marked md2html 都会卡一下,多了估计会影响
|
33
keepeye 2022-01-21 11:30:15 +08:00
cpu 密集型就多搞一些进程啊 这是问题吗
|
34
jguo 2022-01-21 11:31:05 +08:00
别拿 java 和浏览器 js 的概念去套 node
|
35
libook 2022-01-21 12:01:53 +08:00 2
Node 是如何应对高并发场景的?答:异步非阻塞。
JavaScript 的生态根基简单来讲就是语言+API 。 JavaScript 是一门脚本语言,一门语言要想有实际用途就得有能力调用各个系统,那么就需要各个系统面向 JavaScript 提供 API ,比如你计算了 1+2 ,能得出结果 3 ,但你要想看到这个结果就得让操作系统帮你显示出来,于是操作系统(中间省略很多环节)给 JS 提供了个 console API ,你可以使用 console.log 来(中间省略很多环节)调用操作系统把 3 显示出来。 所以 Node 不等于 JS ,JS 语言的执行能力只是 Node 的一项子功能而已。 原生 JavaScript 语言是单线程执行的,但 Node 不是单线程的,Node 为 JS 语言提供了一些 API ,其中大部分都是 IO 相关的 API ,比如网络访问、文件系统访问等。 Node 有一个假设,就是很多应用场景下 IO 操作的工作量要远远大于计算操作。比如大多 Web 应用服务都是响应网络请求( IO 操作),经过简单的逻辑计算,然后进行数据库请求( IO 操作),那么假设只要 CPU 不闲着,IO 负载很可能会比 CPU 负载先用满。 Node 如何做到让 CPU 不闲着?答:计算单线程执行,IO 多线程执行(异步),但计算可以不等着 IO 完成(异步非阻塞)。 不调用任何 API ,纯进行 JS 计算,比如算斐波那契数列,1+2=3,2+3=5……这个只能单线程执行,算 2+3=5 的时候必须等着 1+2 出结果,只不过此时 CPU 并没有闲着而已。 如果在计算出每一个数字的时候,把数字写到硬盘上,这个写硬盘的操作就是 IO 操作; 假设没有异步非阻塞机制,应该是这样的:计算 1+2 ,得出 3 ,执行将 3 写入硬盘,等待写入完成,写入完成后计算 2+3……CPU 在等待的时候是闲着的,时间基本浪费在等待将 3 写入硬盘。 现在 Node 给你了一个能力,就是你可以在向硬盘写入 3 的时候选择不等着它完成,直接继续算 2+3 ,这就相当于有 1 个线程在不停算斐波那契数列,额外还有多个线程帮你把每个结果存硬盘。 回到题主的场景描述,Node 接收到一个请求之后,如果进行简单逻辑计算后就直接操作数据库( IO 操作)或应答( IO 操作)的话,可以选择不等着 IO 操作完成,继续处理下一个请求,等某个 IO 操作完成了就会回来调用后续的 JS 程序。 但如果执行的是异常复杂的计算,比如视频转码,如果是在处理请求的线程里做的话,一定会抢占预期用于处理请求的 CPU 时间,导致请求“卡住”。不过你猜怎么着,Node 其实是提供了多线程 API ( Worker threads )和多进程 API ( Child process ),你完全可以像其他语言那样使用多线程和多进程来进行优化。除此之外 Node 还提供了面向 C/C++的 N-API 以及面向很多语言的 WebAssembly ,在需要极端计算性能的场景下不至于完全放弃 JS 技术栈。 |
36
13Sl 2022-01-21 12:51:09 +08:00
在设计业务逻辑时, 一般不会把需要 1s 处理时间得超重型任务的触发时机留给外界触发, 这种一般在设计上就会定时或使用其他方法手动触发.
node 在除非是在处理纯计算类的代码, 一般都会有时机切换到其他任务处理过程上(比较典型的切换代码标志就是 await), 所以一般来说很难产生像 1 楼所描述的处理请求按顺序等待的情况. |
37
Austaras 2022-01-21 16:20:31 +08:00
我觉得最好还是诚实一点:应对不了。。。
|
39
KouShuiYu 2022-01-21 17:43:36 +08:00
首先:一个请求卡住了,后续其他请求并不会卡住,触发是第一个卡住的原因是因为( CPU 密集型计算)
你可以试试 先访问 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}`); }); |
40
coseylee 2022-03-03 14:25:56 +08:00
@KouShuiYu 如果将 await setTimeout(t) 换成 while(true) 的话,是无法接受并处理之后的请求。之所以 await setTimeout(t) 不会将主线程占用的原因是它属于定时器,是有特殊的调度处理的,不会占用主线程。
|
41
KouShuiYu 2022-03-03 16:27:45 +08:00
@coseylee 只有不是 CPU 密集型计算(比如从 1 循环加到一亿 )都不会卡着,你可以试试比如说请求、读文件等等
const http = require('http'); const { URL } = require('url'); const { setTimeout } = require('timers/promises'); const hostname = '127.0.0.1'; const port = 3001; function fetch(url) { return new Promise((resolve) => { http.get(url, (res) => resolve(res)); }) } const server = http.createServer(async (req, res) => { const t = new URL(`http://${hostname}:${port}${req.url}`).searchParams.get('t'); // 模拟耗时操作 await fetch(`http://127.0.0.1:3000/?t=${t}`); res.end(`Hello World:${t}`); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}`); }); |
42
coseylee 2022-03-04 09:18:21 +08:00
@KouShuiYu 是的,这些都是异步操作,会在线程池处理。针对题主所问,如果长时间阻塞主线程,的确无法再接受请求进来。
|
43
Makabaka01 2022-06-19 10:24:05 +08:00
如果你没有计算耗时的操作,Node 会在相当短的时间把控制权交给下一个请求,直到你的数据库任务、或是网络请求完成时才会继续阻塞程序
|