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

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

还有,单线程能适应这么大的数量吗?请记住,我刚刚开始学习Node.js。


当前回答

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

我可能误解了您在这里所说的内容,但是“一次一个”听起来您可能没有完全理解基于事件的架构。

在“传统的”(非事件驱动的)应用程序体系结构中,流程花费大量时间等待某些事情发生。在基于事件的架构(如Node.js)中,进程不只是等待,它可以继续其他工作。

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

在每一个阶段,大部分时间都花在了等待数据从另一端到达——在JS主线程中处理的实际时间通常是相当少的。

当一个I/O对象(例如网络连接)的状态发生变化,需要处理(例如在套接字上接收数据,套接字变得可写等)时,主Node.js JS线程会被唤醒,并显示需要处理的项列表。

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

下一次它被唤醒时,很可能是由于需要处理不同的I/O对象——例如,不同的网络连接。每次都运行相关的回调,然后它回到睡眠状态,等待其他事情发生。

重要的一点是不同请求的处理是交错的,它不会从头到尾处理一个请求,然后再转移到下一个请求。

在我看来,这样做的主要好处是,一个慢速的请求(例如,你试图通过2G数据连接向移动电话设备发送1MB的响应数据,或者你正在进行一个非常慢的数据库查询)不会阻止更快的请求。

在传统的多线程web服务器中,您通常会为每个正在处理的请求设置一个线程,并且它只处理该请求,直到完成。如果有很多慢速请求会发生什么?最终会有大量线程处理这些请求,而其他请求(可能是可以很快处理的非常简单的请求)则排在它们后面。

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

我不会说基于事件的系统在每种情况下或每种工作负载下都更快——它们在I/ o约束的工作负载上工作得很好,但在cpu约束的工作负载上就不那么好了。

其他回答

多线程阻塞系统的阻塞部分使其效率较低。被阻塞的线程在等待响应期间不能用于其他任何事情。

而非阻塞单线程系统则充分利用了它的单线程系统。

见下图: 在这里,在厨房门口等待或在顾客挑选食物时等待,是“阻塞”了服务员的全部能力。在计算系统的意义上,它可以等待IO,或DB响应或任何阻塞整个线程的东西,即使线程在等待时能够进行其他工作。

让我们看看非阻塞是如何工作的:

在非阻塞系统中,服务员只接单和上菜,不在任何地方等待。他分享了他的手机号码,以便在他们完成订单后给他们回电话。同样地,他将自己的电话号码分享给厨房,以便在订单准备就绪时回复。

这就是Event循环在NodeJS中的工作方式,并且比阻塞多线程系统执行得更好。

添加slebetman的答案,以便更清楚地了解在执行代码时发生了什么。

The internal thread pool in nodeJs just has 4 threads by default. and its not like the whole request is attached to a new thread from the thread pool the whole execution of request happens just like any normal request (without any blocking task) , just that whenever a request has any long running or a heavy operation like db call ,a file operation or a http request the task is queued to the internal thread pool which is provided by libuv. And as nodeJs provides 4 threads in internal thread pool by default every 5th or next concurrent request waits until a thread is free and once these operations are over the callback is pushed to the callback queue. and is picked up by event loop and sends back the response.

现在这里有另一个信息,它不是单一的回调队列,有很多队列。

NextTick队列 微任务队列 定时器队列 IO回调队列(请求,文件操作,数据库操作) IO轮询队列 检查Phase queue或setimmediation 关闭处理程序队列

每当有请求时,代码就按照这个回调队列的顺序执行。

它不像当有一个阻塞请求时,它会附加到一个新线程。默认情况下只有4个线程。这是另一个排队过程。

在代码中,无论何时发生像文件读取这样的阻塞进程,然后调用一个从线程池中利用线程的函数,一旦操作完成,回调被传递到各自的队列,然后按顺序执行。

所有内容都基于回调的类型进行排队,并按照上面提到的顺序进行处理。

下面是来自这篇媒体文章的一个很好的解释:

给定一个NodeJS应用程序,由于Node是单线程的,假设处理涉及Promise。所有这些都需要8秒,这是否意味着在这个请求之后的客户端请求将需要等待8秒? 不。NodeJS事件循环是单线程的。NodeJS的整个服务器架构不是单线程的。

Before getting into the Node server architecture, to take a look at typical multithreaded request response model, the web server would have multiple threads and when concurrent requests get to the webserver, the webserver picks threadOne from the threadPool and threadOne processes requestOne and responds to clientOne and when the second request comes in, the web server picks up the second thread from the threadPool and picks up requestTwo and processes it and responds to clientTwo. threadOne is responsible for all kinds of operations that requestOne demanded including doing any blocking IO operations.

线程需要等待阻塞IO操作的事实使其效率低下。使用这种模型,web服务器只能处理与线程池中线程数量相同的请求。

NodeJS Web Server maintains a limited Thread Pool to provide services to client requests. Multiple clients make multiple requests to the NodeJS server. NodeJS receives these requests and places them into the EventQueue . NodeJS server has an internal component referred to as the EventLoop which is an infinite loop that receives requests and processes them. This EventLoop is single threaded. In other words, EventLoop is the listener for the EventQueue. So, we have an event queue where the requests are being placed and we have an event loop listening to these requests in the event queue. What happens next? The listener(the event loop) processes the request and if it is able to process the request without needing any blocking IO operations, then the event loop would itself process the request and sends the response back to the client by itself. If the current request uses blocking IO operations, the event loop sees whether there are threads available in the thread pool, picks up one thread from the thread pool and assigns the particular request to the picked thread. That thread does the blocking IO operations and sends the response back to the event loop and once the response gets to the event loop, the event loop sends the response back to the client.

NodeJS比传统的多线程请求响应模型好在哪里? 在传统的多线程请求/响应模型中,每个客户端都得到一个不同的线程,而在NodeJS中,更简单的请求都直接由EventLoop处理。这是线程池资源的优化,并且没有为每个客户机请求创建线程的开销。

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

Clients Send request to Web Server. Node JS Web Server internally maintains a Limited Thread pool to provide services to the Client Requests. Node JS Web Server receives those requests and places them into a Queue. It is known as “Event Queue”. Node JS Web Server internally has a Component, known as “Event Loop”. Why it got this name is that it uses indefinite loop to receive requests and process them. Event Loop uses Single Thread only. It is main heart of Node JS Platform Processing Model. Event Loop checks any Client Request is placed in Event Queue. If not then wait for incoming requests for indefinitely. If yes, then pick up one Client Request from Event Queue Starts process that Client Request If that Client Request Does Not requires any Blocking IO Operations, then process everything, prepare response and send it back to client. If that Client Request requires some Blocking IO Operations like interacting with Database, File System, External Services then it will follow different approach Checks Threads availability from Internal Thread Pool Picks up one Thread and assign this Client Request to that thread. That Thread is responsible for taking that request, process it, perform Blocking IO operations, prepare response and send it back to the Event Loop very nicely explained by @Rambabu Posa for more explanation go throw this Link

在slebetman的回答中补充: 当你说Node.JS可以处理10,000个并发请求时,它们本质上是非阻塞请求,即这些请求主要与数据库查询有关。

在内部,Node.JS的事件循环正在处理一个线程池,其中每个线程处理一个非阻塞请求,事件循环在将工作委派给线程池中的一个线程后继续侦听更多的请求。当其中一个线程完成工作时,它向事件循环发送一个信号,它已经完成,也就是回调。事件循环然后处理此回调并发回响应。

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