我不是Node程序员,但我对单线程非阻塞IO模型如何工作感兴趣。
在我阅读了理解node-js-event-loop这篇文章之后,我真的很困惑。
文中给出了模型的一个例子:
c.query(
'SELECT SLEEP(20);',
function (err, results, fields) {
if (err) {
throw err;
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<html><head><title>Hello</title></head><body><h1>Return from async DB query</h1></body></html>');
c.end();
}
);
Que: When there are two requests A(comes first) and B since there is only a single thread, the server-side program will handle the request A firstly: doing SQL querying is asleep statement standing for I/O wait. And The program is stuck at the I/O waiting, and cannot execute the code which renders the web page behind. Will the program switch to request B during the waiting? In my opinion, because of the single thread model, there is no way to switch one request from another. But the title of the example code says that everything runs in parallel except your code.
(注:我不确定我是否误解了代码,因为我有
从未使用过Node。)在等待过程中,节点如何将A切换到B ?并且可以
你解释了Node的单线程非阻塞IO模型
简单的方法吗?如果你能帮助我,我会很感激的。:)
好吧,为了给出一些观点,让我比较node.js和apache。
Apache是一个多线程的HTTP服务器,对于服务器接收到的每个请求,它都会创建一个单独的线程来处理该请求。
Node.js是事件驱动的,从单个线程异步处理所有请求。
当在apache上接收到A和B时,会创建两个线程来处理请求。每个服务器分别处理查询,每个服务器在提供页面之前等待查询结果。该页面只在查询完成之前提供。查询获取阻塞是因为服务器在收到结果之前无法执行线程的其余部分。
在node中,c.query是异步处理的,这意味着当c.query获取A的结果时,它会跳转到处理B的c.query,当A的结果到达时,它会将结果发送回回回调,后者会发送响应。Node.js知道在取回完成时执行回调。
在我看来,因为它是一个单线程模型,所以没有办法
从一个请求切换到另一个请求。
实际上,节点服务器一直都在为你做这件事。要进行切换,(异步行为)您将使用的大多数函数将具有回调。
Edit
SQL查询来自mysql库。它实现回调样式以及事件发射器来排队SQL请求。它不异步执行它们,这是由提供非阻塞I/O抽象的内部libuv线程完成的。查询的步骤如下:
打开一个到db的连接,连接本身可以异步进行。
一旦连接db,查询就被传递到服务器。查询可以排队。
主事件循环通过回调或事件得到完成通知。
主循环执行回调/事件处理程序。
以类似的方式处理对http服务器的传入请求。内部线程架构是这样的:
c++线程是做异步I/O(磁盘或网络)的libuv线程。主事件循环在将请求分派到线程池后继续执行。它可以接受更多的请求,因为它不会等待或休眠。SQL查询/HTTP请求/文件系统读取都是以这种方式发生的。
事件循环允许Node.js执行非阻塞I/O操作——尽管JavaScript是单线程的——只要有可能就将操作卸载给系统内核。可以将事件循环看作管理器。
新请求被发送到队列中,并由同步事件解复用器监视。如您所见,每个操作处理程序也已注册。
Then those requests are sent to the thread pool (Worker Pool) synchronously to be executed. JavaScript cannot perform asynchronous I/O operations. In browser environment, browser handles the async operations. In node environment, async operations are handled by the libuv by using C++. Thread's pool default size is 4, but it can be changed at startup time by setting the UV_THREADPOOL_SIZE environment variable to any value (maximum is 128). thread pool size 4 means 4 requests can get executed at a time, if event demultiplexer has 5 requsts, 4 would be passed to thread pool and 5th would be waiting. Once each request gets executed, result is returned to the `event demultiplexer.
当一组I/O操作完成时,事件解复用器将一组对应的事件推入事件队列。
Handler是回调。现在,事件循环监视事件队列,如果有准备好的事件,它将被推入堆栈以执行回调。记住,回调最终是在堆栈上执行的。请注意,一些回调对其他回调具有优先级,事件循环确实根据它们的优先级选择回调。
好吧,为了给出一些观点,让我比较node.js和apache。
Apache是一个多线程的HTTP服务器,对于服务器接收到的每个请求,它都会创建一个单独的线程来处理该请求。
Node.js是事件驱动的,从单个线程异步处理所有请求。
当在apache上接收到A和B时,会创建两个线程来处理请求。每个服务器分别处理查询,每个服务器在提供页面之前等待查询结果。该页面只在查询完成之前提供。查询获取阻塞是因为服务器在收到结果之前无法执行线程的其余部分。
在node中,c.query是异步处理的,这意味着当c.query获取A的结果时,它会跳转到处理B的c.query,当A的结果到达时,它会将结果发送回回回调,后者会发送响应。Node.js知道在取回完成时执行回调。
在我看来,因为它是一个单线程模型,所以没有办法
从一个请求切换到另一个请求。
实际上,节点服务器一直都在为你做这件事。要进行切换,(异步行为)您将使用的大多数函数将具有回调。
Edit
SQL查询来自mysql库。它实现回调样式以及事件发射器来排队SQL请求。它不异步执行它们,这是由提供非阻塞I/O抽象的内部libuv线程完成的。查询的步骤如下:
打开一个到db的连接,连接本身可以异步进行。
一旦连接db,查询就被传递到服务器。查询可以排队。
主事件循环通过回调或事件得到完成通知。
主循环执行回调/事件处理程序。
以类似的方式处理对http服务器的传入请求。内部线程架构是这样的:
c++线程是做异步I/O(磁盘或网络)的libuv线程。主事件循环在将请求分派到线程池后继续执行。它可以接受更多的请求,因为它不会等待或休眠。SQL查询/HTTP请求/文件系统读取都是以这种方式发生的。
Node.js基于事件循环编程模型。事件循环在单个线程中运行,反复等待事件,然后运行订阅这些事件的任何事件处理程序。例如,事件可以是
计时器等待完成
下一个数据块已经准备好写入该文件
有一个新的HTTP请求即将到来
所有这些都在单个线程中运行,没有JavaScript代码是并行执行的。只要这些事件处理程序很小,并且等待更多的事件本身,一切都能很好地工作。这允许单个Node.js进程并发处理多个请求。
(在事件起源的地方有一点魔力。其中一些涉及并行运行的低级工作线程。)
在这个SQL案例中,在进行数据库查询和在回调中获得结果之间发生了很多事情(事件)。在此期间,事件循环不断向应用程序注入生命,并一次一个小事件推进其他请求。因此,并发地服务多个请求。
根据:“来自10,000英尺的事件循环- Node.js背后的核心概念”。
函数c.query()有两个参数
c.query("Fetch Data", "Post-Processing of Data")
在这种情况下,“获取数据”操作是一个DB-Query,现在这可能由Node.js通过衍生出一个工作线程来处理,并赋予它执行DB-Query的任务。(记住Node.js可以在内部创建线程)。这使得函数能够立即返回,没有任何延迟
第二个参数“Post-Processing of Data”是一个回调函数,节点框架注册这个回调函数,并由事件循环调用。
因此,语句c.query (paramenter1, parameter2)将立即返回,使node能够满足另一个请求。
附注:我刚刚开始理解节点,实际上我想把这作为对@Philip的评论,但由于没有足够的声誉点,所以把它作为答案写了下来。