为什么Node.js是单线程的?[关闭]


255

在基于PHP(或Java / ASP.NET / Ruby)的Web服务器中,每个客户端请求都在新线程上实例化。但是在Node.js中,所有客户端都在同一线程上运行(它们甚至可以共享相同的变量!)我知道I / O操作是基于事件的,因此它们不会阻塞主线程循环。

我不明白为什么Node的作者选择了它为单线程?这使事情变得困难。例如,我无法运行CPU密集型功能,因为它阻塞了主线程(并且阻塞了新的客户端请求),因此我需要产生一个进程(这意味着我需要创建一个单独的JavaScript文件并在其上执行另一个节点进程)它)。但是,在PHP cpu中,密集型任务不会阻止其他客户端,因为正如我提到的那样,每个客户端都在不同的线程上。与多线程Web服务器相比,它有什么优势?

注意:我已经使用集群解决了这个问题,但这并不是很漂亮。


12
我最近观看了一段不错的视频(29分钟),解释了Node背后的一些理论。我甚至想到CPU密集型任务,并简要如何处理它们的家伙会谈:youtube.com/watch?v=L0pjVcIsU6A
whirlwin

24
您可能知道这一点,但请注意,Node.js不是单线程的。您的JavaScript代码运行单线程,但是IO操作和插件可以执行的其他操作会耗尽线程池。Node.js为您提供了多线程的许多好处,而无需处理多线程代码。而且,Node.js贡献者没有选择JavaScript的单线程性质,而是JavaScript的作者选择了。我想不出JS可以在多线程上下文中工作的方式,但是即使有,V8也不是以Node.js用作其JavaScript引擎的方式编写的。
布拉德

5
PHP比JavaScript多单线程。您可能正在考虑诸如FastCGI或mod_php之类的服务器模块。因此,您实际上是将Node.js与Apache,Nginx或IIS进行了比较,而不是与PHP,Java或Ruby进行了比较。
阿尔瓦罗·冈萨雷斯

34
节点不是单线程的。这是一个普遍的误解。即使简单,我的系统也会node -e 'setTimeout(()=>{},1000);' & ps -T h $! | wc -l; kill $!显示五个线程。主事件循环是单线程的(如果不是,则没有多大意义),但是Node是大量的多线程,并且您可以根据需要编写多线程的单进程应用程序。我想写一个全面的答案,但有些人决定关闭您的问题,所以我不能。我正在投票重新打开它。如果获得更多票数并重新开放,请在评论中提及我。
rsp

2
@rsp感谢您的评论,但我的意思是在主线程中与I / O不相关。如果您正在执行与CPU相关的操作(例如big for循环),则服务器将停止处理连接。表示服务器当时无法使用。因此,我们只能使用集群之类的黑客工具来做如此简单的事情,而不是像大多数服务器那样固有地对每个连接进行线程化。jxcore.com试图解决此问题,但随后它使一个人使用了特殊的/修改的节点插件,这实际上使它对我不可用。
foreyez '16

Answers:


292

Node.js是作为异步处理中的实验而显式创建的。从理论上讲,在典型的Web负载下,与典型的基于线程的实现相比,在单个线程上执行异步处理可以提供更高的性能和可伸缩性。

你知道吗?我认为理论已经被证实。与Apache或IIS或其他基于线程的服务器相比,不执行CPU密集型工作的node.js应用程序可以运行数千个并发连接。

单线程异步性质的确使事情变得复杂。但是,老实说,您是否认为它比线程处理还要复杂?一种比赛条件可能会破坏您的整个月!或者由于某处的某些设置而清空线程池,并观察响应时间变慢以进行爬网!更不用说死锁,优先级倒置以及与多线程相关的所有其他回旋。

最后,我不认为它普遍好坏。这是不同的,有时更好,有时则不是。使用正确的工具完成工作。


26
但是,Web服务器通常会执行大量CPU密集型工作,而不仅仅是获取数据库。我们需要处理获取的内容,并在将其提供给客户之前先进行大量的业务逻辑处理。
foreyez

22
因此,只是生成工,好吧!这就是Node.js的全部内容。繁重的工作可以在另一个进程中运行,您对其进行处理将导致轻量级回调。
MaiaVictor

7
这样做的问题是每个工人都有一个os级进程在运行。您将使用“ ps”命令看到它们。因此,这可能意味着同时在计算机上运行数千个进程-太疯狂了!
foreyez

9
@foreyez,您不需要每个用户一个进程。您可以选择如何分配负载。另外,并非每个人都在做大量占用CPU的工作。Node是工作的工具……也许不是您的工作,而是很多工作。
布拉德

15
实际上,我希望@foreyez支持该声明,即“ Web服务器通常要占用大量CPU密集型设备”。以我的经验,他们没有。也许我对“ CPU密集型”的定义不同于他的定义。将产品数据转换为UI不会占用大量CPU,也不会计算订单等。大多数网络都是事务性的。CPU密集型的东西是诸如转换视频,转换图像格式等之类的东西。这很大程度上归功于文件I / O,实际上,节点运行得很好。并使其易于卸载到另一个专用于转换的过程。
保罗

62

服务器的“每个请求一个线程”模型的问题在于,与事件循环线程模型相比,它们在几种情况下无法很好地扩展。

通常,在I / O密集型方案中,请求会花费大部分时间等待I / O完成。在此期间,在“每个请求一个线程”模型中,链接到该线程的资源(例如内存)未使用,内存是限制因素。在事件循环模型中,循环线程选择下一个要处理的事件(I / O完成)。因此,线程总是很忙(如果正确编程的话)。

事件循环模型看起来很新鲜,所有问题的解决方案都取决于您需要解决的情况,但是要使用哪种模型。如果您具有密集型I / O方案(例如代理),则将以事件基模型为准,而并行进程数量较少的CPU密集型方案将最适合基于线程的模型。

在现实世界中,大多数情况都将处于中间位置。您将需要在可伸缩性的实际需求与开发复杂性之间找到平衡,以找到正确的体系结构(例如,具有事件基前端,该事件基前端可以委派给后端执行CPU密集型任务。前端将使用很少的资源来等待任务与任何分布式系统一样,它需要付出一些努力才能使其正常工作。

如果您不费吹灰之力地寻找适合任何情况的银色子弹,那么最终您将获得一枚子弹。


8
由于缺少v8多线程支持,因此Node.js仅限于仅事件处理。好吧,JavaScript语言本身缺少所需的功能,因此任何实现都会变得棘手。我认为这是Node.js的主要根源。使用其他语言,您可以选择所需的内容。或两种模型的某种混合,例如java NIO。
FrameGrace

2
@Kazaag,现代Web服务器确实维护线程池。他们不只是愚蠢地在每次页面加载时生成一个新线程。这些是较旧的Web服务器。
Pacerier '17

1
@Pacerier我从未说过会产生一个新线程,但是每个线程都会分配给一个请求,直到请求完成为止。
Kazaag

2
@Kazaag绝对不是“在请求完成之前将每个线程分配给一个请求”的一般规则。即在.Net(包括处理HTTP请求)中,可以并且应该使用异步(基于任务)编程,这将在等待I / O和其他异步操作完成时释放线程。这也适用于高级编程,即MVC / API控制器。因此,实际上可能有20个HTTP请求待处理,但只有一个活动线程。
user3285954 '18

29

长话短说,节点来自内部单线程的V8。有一些方法可以解决CPU密集型任务的约束。

在某一时刻(0.7),作者试图引入隔离作为实现多个计算线程的一种方式,但最终被删除:https : //groups.google.com/forum/#!msg/ nodejs/zLzuo292hX0/ F7gqfUiKi2sJ


您是否有关于此“隔离”的更多信息?
Pacerier '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.