提问者:小点点

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


我理解Node.js使用单线程和事件循环来处理请求,一次只处理一个请求(这是非阻塞的)。但是,它是如何工作的,比如说10,000个并发请求。事件循环将处理所有请求?那会不会太久了?

我还不明白它怎么能比多线程web服务器更快。我理解多线程web服务器在资源(内存,CPU)上会更贵,但它不还是会更快吗?我可能错了;请解释这个单线程如何在大量请求中更快,以及它在处理大量请求(如10,000)时通常做什么(在高级)。

而且,单线程在这么大的量下能很好地扩展吗?请记住,我才刚刚开始学习Node.js。


共3个答案

匿名用户

如果一定要问这个问题,那么您可能不熟悉大多数web应用程序/服务的功能。您可能认为所有软件都是这样做的:

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

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

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时间来等待数据库返回。

多线程网络应用程序处理上述工作负载的方式如下:

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的时候运行一些代码呢?这样,每个请求仍将获得与多线程应用程序相同的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,这会减慢我们的速度。

看似神秘的事情是,上述两种方法是如何以“并行”方式运行工作负载的?答案是数据库是线程的。因此,我们的单线程应用程序实际上利用了另一个进程的多线程行为:数据库。

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

单线程应用程序的另一个缺陷是它只使用一个CPU核心。因此,如果您有一个四核服务器(现在并不少见),您不会使用其他3个核。

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

一个最终使多线程变得更糟的用例是当您需要在您的线程中运行另一种脚本语言时。首先通常需要malloc该语言的整个运行时,然后需要malloc脚本使用的变量。

因此,如果您正在用C,go或Java编写网络应用程序,那么线程开销通常不会太大。如果您正在编写为PHP或Ruby服务的C web服务器,那么用javascript或Ruby或Python编写速度更快的服务器是非常容易的。

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

一些单线程体系结构也使用混合方法。您可以启动多个应用程序,而不是从单个进程启动多个线程--例如,在四核计算机上启动4个Node.js服务器。然后使用负载均衡器在进程之间分配工作负载。

实际上,这两种方法在技术上是完全相同的,互为镜像。

匿名用户

您似乎在想的是,大部分处理都是在节点事件循环中处理的。节点实际上将I/O工作转移到线程。I/O操作通常要比CPU操作花费更长的数量级,那么为什么要让CPU等待呢?此外,操作系统已经可以很好地处理I/O任务。事实上,因为节点不在周围等待,所以它实现了更高的CPU利用率。

通过类比,将NodeJS想象成一个服务员在接受客户的点菜,而I/O厨师则在厨房里准备他们的点菜。其他系统有多个厨师,他们接受顾客的点菜,准备饭菜,清理桌子,然后才处理下一个顾客。

匿名用户

单线程事件循环模型处理步骤:

>

  • 客户端向Web服务器发送请求。

    NodeJSWeb服务器在内部维护一个有限的线程池,以便为客户机请求提供服务。

    NodeJSWeb服务器接收这些请求并将它们放入队列。它被称为“事件队列”。

    NodeJSWeb服务器内部有一个组件,称为“事件循环”。它之所以得到这个名字,是因为它使用不定循环来接收请求并处理它们。

    事件循环仅使用单线程。它是节点JS平台处理模型的核心。

    事件循环检查放置在事件队列中的任何客户端请求。如果没有,则无限期地等待传入的请求。

    如果是,则从事件队列中提取一个客户端请求

    1. 开始处理客户端请求
    2. 如果客户端请求不需要任何阻塞IO操作,则处理所有内容,准备响应并将其发送回客户端。
    3. 如果客户端请求需要一些阻塞IO操作,例如与数据库,文件系统,外部服务交互,那么它将遵循不同方法

    该线程负责接受请求,处理请求,执行阻塞IO操作,准备响应并将其发送回事件循环

    很好地解释了@rambabu Posa想要更多的解释,请扔这个链接