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

标签 node.js

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

我无法理解(还)它如何比多线程 Web 服务器更快。我知道多线程 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 应用程序或任何以数据库为后端的应用程序的工作方式。 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 通常获胜的地方。

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

因此,如果您使用 C 或 go 或 java 编写网络应用程序,那么线程的开销通常不会太糟糕。如果您正在编写一个 C Web 服务器来为 PHP 或 Ruby 提供服务,那么用 javascript、Ruby 或 Python 编写一个更快的服务器非常容易。

混合方法

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

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

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

关于node.js - 一般来说,Node.js 如何处理 10,000 个并发请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34855352/

相关文章:

javascript - 语法错误 : Cannot use import statement outside a module when start NodeJS from IntellijIDEA

javascript - 如何防止 `gulp-uglify` 移动我的全局变量?

javascript - 查询字符串模块中的 stringify 在 Jest 测试中未按预期工作

linux - Node 检查器不在 linux 服务器上工作

javascript - 基于 api key 的 Node JS Web 服务

node.js - 将 Sails.js 应用程序部署到 Heroku?

node.js - 在 Openshift 上设置 NODE_ENV 的目的是什么

javascript - 如何在 nodejs 12 中使用私有(private)类字段?

javascript - 苹果电脑 : Node JS Mongo Database Error: Connection Refused

node.js - 为什么 Mongodb 更新有效但 mongoose 没有?