一般来说,Node.js如何处理10,000个并发请求?

我知道Node.js使用单线程和事件循环来处理一次只处理一个请求(非阻塞)的请求。 但是,如何工作,可以说一万个并发请求。 事件循环会处理所有的请求? 这不会花太长时间吗?

我还不能理解它如何比multithreading的Web服务器更快。 我知道multithreading的Web服务器在资源(内存,CPU)方面会更加昂贵,但是它不会更快吗? 我可能是错的; 请解释这种单线程在大量请求中的速度如何,以及在维护大量请求(如10,000)时通常如何处理(高层次)。

而且,这个单线程能够很好地扩展吗? 请记住,我刚开始学习Node.js。

如果你不得不问这个问题,那么你可能不熟悉大多数Web应用程序/服务。 你可能认为所有的软件都这样做:

user do an action │ v application start processing action └──> loop ... └──> busy processing end loop └──> send result to user 

但是,这并不是Web应用程序或实际上任何以数据库作为后端的应用程序的工作方式。 networking应用程序这样做:

 user do an action │ v application start processing action └──> make database request └──> do nothing until request completes request complete └──> send result to user 

在这种情况下,软件花费大部分运行时间,使用0%的CPU时间等待数据库返回。

multithreading的networking应用程序:

multithreadingnetworking应用程序可以像这样处理上述工作负载:

 request ──> spawn thread └──> wait for database request └──> answer request request ──> spawn thread └──> wait for database request └──> answer request request ──> spawn thread └──> wait for database request └──> answer request 

所以线程花费大部分时间使用0%的CPU等待数据库返回数据。 虽然这样做,他们不得不分配一个线程所需的内存,其中包括一个完全独立的程序堆栈为每个线程等。而且,他们将不得不启动一个线程,而不是像开始一个完整的过程一样昂贵,仍然不完全低廉。

单线程事件循环

由于我们大部分时间都是使用0%的CPU,所以当我们不使用CPU时,为什么不运行一些代码呢? 这样,每个请求仍将获得与multithreading应用程序相同数量的CPU时间,但是我们不需要启动一个线程。 所以我们这样做:

 request ──> make database request request ──> make database request request ──> make database request database request complete ──> send response database request complete ──> send response database request complete ──> send response 

实际上,这两种方法都会以大致相同的延迟返回数据,因为这是控制处理的数据库响应时间。

这里的主要优点是我们不需要产生一个新的线程,所以我们不需要做很多很多的malloc,这会减慢我们的速度。

魔术,无形的线程

看似神秘的事情是,上述两种方法如何在“平行”上运行工作量? 答案是数据库是线程的。 所以我们的单线程应用程序实际上是利用另一个进程的multithreading行为:数据库。

单线程方法失败

如果您在返回数据之前需要执行大量CPU计算,则单线程应用程序会失败。 现在,我不是指for循环处理数据库结果。 这仍然主要是O(n)。 我的意思是像傅立叶变换(例如mp3编码),光线追踪(3D渲染)等等。

单线程应用程序的另一个缺陷是它只会使用一个CPU核心。 所以如果你有一个四核服务器(现在并不less见),那么你并没有使用其他3核心。

multithreading方法失败的地方

如果您需要为每个线程分配大量内存,multithreading应用程序将会失败。 首先,RAM使用本身意味着你不能像单线程应用程序那样处理多less请求。 更糟的是,malloc很慢。 分配大量的对象(这在现代Web框架中是很常见的)意味着我们可能最终比单线程应用程序慢。 这是node.js通常胜利的地方。

最终导致multithreading化的一个用例就是当你需要在线程中运行另一种脚本语言的时候。 首先,您通常需要为该语言运行整个运行库,然后您需要malloc脚本使用的variables。

所以,如果你用C语言编写networking应用程序,或者使用java,那么线程的开销通常不会太大。 如果您正在编写C web服务器来为PHP或Ruby提供服务,那么使用JavaScript或Ruby或Python编写更快的服务器非常容易。

混合的方法

一些networking服务器使用混合方法。 例如Nginx和Apache2将其networking处理代码实现为事件循环的线程池。 每个线程运行一个事件循环,同时处理单线程请求,但请求在多个线程之间进行负载平衡。

一些单线程体系结构也使用混合方法。 您可以启动多个应用程序(例如,四核机器上的4个node.js服务器),而不是从一个进程启动多个线程。 然后使用负载平衡器将工作负载分散到进程中。

实际上,这两种方法在技术上是彼此相同的镜像。

你似乎在想,大部分的处理是在节点事件循环中处理的。 节点实际上将I / O工作从农场转移到线程。 I / O操作通常需要比CPU操作更长的时间,为什么CPU要等待呢? 此外,操作系统已经可以很好地处理I / O任务。 事实上,由于Node不会等待,因此可以获得更高的CPU利用率。

举个类比,把NodeJS当做服务员,接受客户的订单,而I / O厨师在厨房里准备。 其他系统有多个厨师,他们接受顾客的订单,准备餐点,清理餐桌,然后才能接待下一位顾客。

我知道Node.js使用单线程和事件循环来处理一次只处理一个请求(非阻塞)的请求。

我可能会误解你在这里所说的,但是“一次一个”听起来像你可能不完全了解基于事件的架构。

在“传统”(非事件驱动)应用程序架构中,这个过程需要花费大量的时间来等待事件的发生。 在基于事件的架构(如Node.js)中,进程不仅仅是等待,还可以继续其他工作。

例如:从客户端获得连接,接受它,读取请求头(在http的情况下),然后开始对请求执行操作。 您可能会阅读请求主体,通常最终会将一些数据发送回客户端(这是对过程的有意简化,只是为了说明这一点)。

在这些阶段的每一个阶段,大部分时间都花在等待一些数据从另一端到达 – 在主JS线程中处理的实际时间通常非常短暂。

当一个I / O对象(比如networking连接)的状态发生变化,需要处理时(例如,在套接字上接收数据,套接字变为可写等),主Node.js JS线程被唤醒需要处理的项目。

它find相关的数据结构并在该结构上发出一些事件,这些事件导致callback被执行,处理传入的数据,或者将更多的数据写入套接字等。一旦所有需要处理的I / O对象处理后,主Node.js JS线程将再次等待,直到它被告知更多的数据可用(或其他一些操作已完成或超时)。

下一次唤醒时,可能是由于需要处理不同的I / O对象,例如不同的networking连接。 每一次,相关的callback运行,然后回到睡觉等待其他事情发生。

重要的一点是不同请求的处理是交错的,它不处理一个请求从一开始到结束,然后转到下一个请求。

在我看来,这样做的主要优势在于,一个缓慢的请求(例如,您试图通过2G数据连接向移动电话设备发送1MB的响应数据,或者您正在做一个非常慢的数据库查询)阻止更快的人。

在传统的multithreadingWeb服务器中,通常每个请求都有一个线程处理,并且只处理该请求,直到完成。 如果你有很多缓慢的请求会发生什么? 最后,有很multithreading处理这些请求,而其他请求(可能是非常简单的请求,可能会很快处理)就会排在其后面。

除了Node.js之外,还有很多其他基于事件的系统,与传统模型相比,它们往往具有相似的优点和缺点。

我不会声称基于事件的系统在每种情况下或每个工作负载都更快 – 它们往往适用于I / O约束的工作负载,而不适合CPU约束的工作负载。

添加到slebetman答案:当你说Node.JS可以处理10,000个并发请求时,它们基本上是非阻塞请求,即这些请求主要涉及到数据库查询。

Node.JS event loopNode.JS正在处理一个thread pool ,每个线程处理一个non-blocking request ,事件循环在委托给thread pool一个线程之后继续侦听更多的请求。 当一个线程完成工作时,它发送一个信号给event loop ,它已经完成了又名callbackEvent loop然后处理这个callback并且发回响应。

由于您是nextTick ,请阅读有关nextTick更多信息,以了解事件循环如何在内部工作。 阅读http://javascriptissexy.com上的博客,当我开始使用JavaScript / NodeJS时,他们对我非常有帮助。