单线程非阻塞 IO 模型如何在 Node.js 中工作 [英] How the single threaded non blocking IO model works in Node.js

查看:29
本文介绍了单线程非阻塞 IO 模型如何在 Node.js 中工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不是 Node 程序员,但我对单线程非阻塞 IO 模型的工作原理很感兴趣.在我阅读文章后understanding-the-node-js-event-loop,我真的很困惑.它给出了模型的示例:

c.query('选择睡眠(20);',功能(错误,结果,字段){如果(错误){抛出错误;}res.writeHead(200, {'Content-Type': 'text/html'});res.end('<html><head><title>你好</title></head><body><h1>从异步数据库查询返回</h1></body><;/html>');c.end();});

队列:当有两个请求A(先来)和B时,因为只有一个线程,服务器端程序会先处理请求A:做SQL查询是sleep语句代表 I/O 等待.并且程序卡在I/O等待,无法执行渲染网页的代码.程序在等待过程中会切换到请求B吗?在我看来,由于单线程模型,无法从另一个请求切换一个请求.但是示例代码的标题说除了您的代码之外,所有东西都是并行运行的.

<块引用>

(P.S 我不确定我是否误解了代码,因为我有从来没用过 Node.)Node 在等待过程中如何将 A 切换到 B?并且可以你解释了 Node 的单线程非阻塞 IO 模型简单的方法?如果你能帮助我,我将不胜感激.:)

解决方案

Node.js 建立在 libuv,一个跨平台的库,为支持的操作系统(至少是 Unix、OS X 和 Windows)提供的异步(非阻塞)输入/输出抽象 API/系统调用.

异步IO

在这种编程模型中,对由文件系统管理的设备和资源(套接字、文件系统等)的打开/读/写操作不要阻塞调用线程(就像在典型的同步类似 c 的模型),并且只需标记进程(在内核/操作系统级数据结构中),以便在新数据或事件可用时得到通知.如果是类似网络服务器的应用程序,则流程负责确定通知事件属于哪个请求/上下文,并从那里继续处理请求.请注意,这必然意味着您将位于与向操作系统发起请求的堆栈帧不同的堆栈帧上,因为后者必须让步给进程的调度程序,以便单线程进程处理新事件.

我描述的模型的问题在于它对于程序员来说并不熟悉且难以推理,因为它本质上是非顺序的.您需要在函数 A 中发出请求,并在不同的函数中处理结果,而 A 的本地人通常不可用."

Node 的模型(Continuation Passing Style 和 Event Loop)

Node 利用 javascript 的语言特性解决了这个问题,通过诱导程序员采用某种编程风格,使这个模型看起来更加同步.每个请求 IO 的函数都有一个类似于 function (... parameters ..., callback) 的签名,并且需要给出一个回调,该回调将在请求的操作完成时调用(请记住,大部分时间都花在等待操作系统发出完成信号上 - 可以花在做其他工作上的时间).Javascript 对闭包的支持允许您使用在回调主体内的外部(调用)函数中定义的变量 - 这允许在将由节点运行时独立调用的不同函数之间保持状态.另请参阅连续传递风格.

此外,在调用产生 IO 操作的函数后,调用函数通常会返回控制到节点的事件循环.这个循环将调用下一个被安排执行的回调或函数(很可能是因为操作系统通知了相应的事件)——这允许并发处理多个请求.

您可以将节点的事件循环视为有点类似于内核的调度程序:内核将在其挂起的 IO 完成后调度执行一个阻塞的线程,而节点将在相应的事件发生时调度回调发生了.

高并发,无并行

作为最后的评论,短语除了您的代码之外的所有内容都并行运行"在捕捉节点允许您的代码处理来自单个线程的数十万个打开套接字的请求方面做得不错 同时通过在单个执行流中对所有 js 逻辑进行多路复用和排序(即使说一切都并行运行"在这里可能不正确 - 请参阅 并发与并行 - 有什么区别?).这对于 webapp 服务器非常有效,因为大部分时间实际上都花在等待网络或磁盘(数据库/套接字)上,并且逻辑并不是真正的 CPU 密集型 - 也就是说:这适用于 IO 绑定工作负载.

I'm not a Node programmer, but I'm interested in how the single threaded non blocking IO model works. After I read the article understanding-the-node-js-event-loop, I'm really confused about it. It gave an example for the model:

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 request 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 stucked 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.

(P.S I'm not sure if I misunderstand the code or not since I have never used Node.)How Node switch A to B during the waiting? And can you explain the single threaded non blocking IO model of Node in a simple way? I would appreciate it if you could help me. :)

解决方案

Node.js is built upon libuv, a cross-platform library that abstracts apis/syscalls for asynchronous (non-blocking) input/output provided by the supported OSes (Unix, OS X and Windows at least).

Asynchronous IO

In this programming model open/read/write operation on devices and resources (sockets, filesystem, etc.) managed by the file-system don't block the calling thread (as in the typical synchronous c-like model) and just mark the process (in kernel/OS level data structure) to be notified when new data or events are available. In case of a web-server-like app, the process is then responsible to figure out which request/context the notified event belongs to and proceed processing the request from there. Note that this will necessarily mean you'll be on a different stack frame from the one that originated the request to the OS as the latter had to yield to a process' dispatcher in order for a single threaded process to handle new events.

The problem with the model I described is that it's not familiar and hard to reason about for the programmer as it's non-sequential in nature. "You need to make request in function A and handle the result in a different function where your locals from A are usually not available."

Node's model (Continuation Passing Style and Event Loop)

Node tackles the problem leveraging javascript's language features to make this model a little more synchronous-looking by inducing the programmer to employ a certain programming style. Every function that requests IO has a signature like function (... parameters ..., callback) and needs to be given a callback that will be invoked when the requested operation is completed (keep in mind that most of the time is spent waiting for the OS to signal the completion - time that can be spent doing other work). Javascript's support for closures allows you to use variables you've defined in the outer (calling) function inside the body of the callback - this allows to keep state between different functions that will be invoked by the node runtime independently. See also Continuation Passing Style.

Moreover, after invoking a function spawning an IO operation the calling function will usually return control to node's event loop. This loop will invoke the next callback or function that was scheduled for execution (most likely because the corresponding event was notified by the OS) - this allows the concurrent processing of multiple requests.

You can think of node's event loop as somewhat similar to the kernel's dispatcher: the kernel would schedule for execution a blocked thread once its pending IO is completed while node will schedule a callback when the corresponding event has occured.

Highly concurrent, no parallelism

As a final remark, the phrase "everything runs in parallel except your code" does a decent job of capturing the point that node allows your code to handle requests from hundreds of thousands open socket with a single thread concurrently by multiplexing and sequencing all your js logic in a single stream of execution (even though saying "everything runs in parallel" is probably not correct here - see Concurrency vs Parallelism - What is the difference?). This works pretty well for webapp servers as most of the time is actually spent on waiting for network or disk (database / sockets) and the logic is not really CPU intensive - that is to say: this works well for IO-bound workloads.

这篇关于单线程非阻塞 IO 模型如何在 Node.js 中工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆