我不是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();
}
);
Run Code Online (Sandbox Code Playgroud)
这是一个问题.当有两个请求A(至上)和B,由于只有一个线程,服务器端程序将处理请求的第一:做SQL查询这是一个睡眠声明站立I/O等待.并且程序停留在I/O等待,并且无法执行呈现网页的代码.程序会在等待期间切换到请求B吗?在我看来,由于单线程模型,没有办法从一个请求切换另一个请求.但是示例代码的标题说"除了代码之外,所有内容都是并行运行".(PS我不确定我是否误解了代码,因为我从未使用过Node.)节点如何在等待期间切换到B?你能用一种简单的方法解释Node的单线程非阻塞IO模型吗?如果你能帮助我,我将不胜感激.:)
Uta*_*aal 370
Node.js构建于libuv之上,libuv是一个跨平台库,它为受支持的操作系统(至少是Unix,OS X和Windows)提供的异步(非阻塞)输入/输出提取apis/syscalls.
在这个编程模型中,由文件系统管理的设备和资源(套接字,文件系统等)上的打开/读/写操作不会阻塞调用线程(如典型的同步c类模型),只需标记当新数据或事件可用时要通知的进程(在内核/ OS级数据结构中).在类似Web服务器的应用程序的情况下,该过程然后负责确定所通知的事件属于哪个请求/上下文并从那里继续处理该请求.请注意,这必然意味着您将与发起请求的操作系统位于不同的堆栈框架上,因为后者必须向进程调度程序提供,以便单个线程进程处理新事件.
我所描述的模型的问题在于它对程序员来说并不熟悉且难以推理,因为它本质上是非顺序的."你需要在函数A中发出请求,并在一个不同的函数中处理结果,其中你的本地人通常不可用."
Node利用javascript的语言功能来解决问题,通过诱导程序员采用某种编程风格,使这个模型更具同步性.请求IO的每个函数都有一个签名,function (... parameters ..., callback)
并且需要给出一个回调函数,该函数将在请求的操作完成时被调用(请记住,大部分时间都花在等待操作系统发出完成信号的时间 - 可以是花在做其他工作).Javascript对闭包的支持允许您使用在回调体内的外部(调用)函数中定义的变量 - 这允许在不同的函数之间保持状态,这些函数将由节点运行时独立调用.另请参见继续传递样式.
此外,在调用产生IO操作的函数之后,调用函数通常将return
控制到节点的事件循环.此循环将调用计划执行的下一个回调或函数(很可能是因为操作系统通知了相应的事件) - 这允许并发处理多个请求.
您可以将节点的事件循环视为与内核的调度程序有些类似:内核将在其挂起的IO完成后计划执行被阻塞的线程,而节点将在相应的事件发生时调度回调.
最后一句话,"除了代码之外的所有内容并行运行"这一短语确实能够捕获节点允许代码通过多路复用和排序所有js 来同时处理来自数十万个开放套接字的请求的点.单个执行流中的逻辑(尽管说"一切并行运行"在这里可能不正确 - 请参阅并发与并行 - 有什么区别?).这对于webapp服务器非常有效,因为大部分时间实际上花在等待网络或磁盘(数据库/套接字)上,并且逻辑实际上不是CPU密集型 - 也就是说:这适用于IO绑定工作负载.
use*_*109 204
好吧,为了给出一些观点,让我将node.js与apache进行比较.
Apache是一个多线程HTTP服务器,对于服务器接收的每个请求,它创建一个处理该请求的单独线程.
另一方面,Node.js是事件驱动的,从单个线程异步处理所有请求.
当在apache上收到A和B时,会创建两个处理请求的线程.每个处理查询单独,每个在服务页面之前等待查询结果.该页面仅在查询完成之前提供.查询提取是阻塞的,因为服务器在收到结果之前无法执行其余的线程.
在节点中,c.query是异步处理的,这意味着当c.query获取A的结果时,它跳转到处理B的c.query,当结果到达A到达时,它将结果发送回回调,后者发送响应.Node.js知道在获取完成时执行回调.
在我看来,因为它是单线程模型,所以无法从一个请求切换到另一个请求.
实际上,节点服务器始终为您执行此操作.要创建开关,(异步行为)您将使用的大多数函数都将具有回调.
SQL查询来自mysql库.它实现了回调样式以及事件发射器以对SQL请求进行排队.它不会异步执行它们,这是由提供非阻塞I/O抽象的内部libuv线程完成的.执行以下步骤以进行查询:
对http服务器的传入请求以类似的方式处理.内部线程架构是这样的:
C++线程是执行异步I/O(磁盘或网络)的libuv线程.在将请求分派给线程池之后,主事件循环继续执行.它可以接受更多请求,因为它不会等待或睡眠.SQL查询/ HTTP请求/文件系统读取都以这种方式发生.
Node.js基于事件循环编程模型.事件循环在单个线程中运行并重复等待事件,然后运行订阅这些事件的任何事件处理程序.事件可以是例如
所有这些都在单线程中运行,并且没有JavaScript代码并行执行.只要这些事件处理程序很小并且等待更多事件本身,一切都很顺利.这允许单个Node.js进程同时处理多个请求.
(事件发生的地方有一点魔力.其中一些涉及并行运行的低级工作线程.)
在这个SQL案例中,在进行数据库查询和在回调中获取结果之间发生了很多事情(事件).在此期间,事件循环不断地将生命投入到应用程序中,并且一次推进其他请求一个小事件.因此,同时提供多个请求.
根据:"10,000ft的事件循环 - Node.js背后的核心概念".
函数c.query()有两个参数
c.query("Fetch Data", "Post-Processing of Data")
Run Code Online (Sandbox Code Playgroud)
在这种情况下,操作"获取数据"是一个DB-Query,现在这可以由Node.js通过产生一个工作线程并给它执行执行DB-Query的任务来处理.(记住Node.js可以在内部创建线程).这使得该功能可以立即返回而不会有任何延迟
第二个参数"数据的后处理"是一个回调函数,节点框架注册这个回调并由事件循环调用.
因此,该语句c.query (paramenter1, parameter2)
将立即返回,使节点能够满足另一个请求.
PS:我刚开始理解节点,实际上我想写这个作为对@Philip的评论, 但由于没有足够的声誉点,所以把它写成答案.
归档时间: |
|
查看次数: |
104548 次 |
最近记录: |