理解Node.js的事件循环

简介:

一篇讲解node.js事件循环的文章。原文出处:点击跳转

在了解node.js之前你首先需要了解的一个基本的论点是:I/O是“昂贵”的。


因此对于当前的编程技术而言,最大的浪费来自于等待I/O的完成。下面列出了改善该问题的几种方式,其中的某个可以帮助你提高性能:

  • 同步:在某一时刻,一次只处理一个请求。但这种情况下,任何一个请求都会“耽误”(阻塞)所有其他的请求。
  • fork一个新进程:对于每个请求,你启动一个新的进程来处理。这种情况下,无法达到很好的扩展,上百个连接就意味着上百个进程的存在。fork()函数是Unix程序员的锤子,因为使用它很方便,所以每个程序都看起来像个钉子一样(都喜欢用锤子拿来敲敲它)。所以,经常造成过度使用,而有些过往矫正。
  • 线程:开启一个新的线程来处理每个请求。这种方式很简单,并且对于内核来讲使用线程也比fork进程来得“亲切”,因为通常线程花费比进程更少的开销。缺点:你的机子可能不支持基于线程编程,并且基于线程的程序,其复杂度增长得非常快,同时你还会有对访问共享资源的担忧。

你需要了解的第二个论点是:被线程处理的每个连接都是“内存昂贵的”。

Apache是采用多线程处理请求的。它对于每个请求“孵化”出一个线程(或者进程,这取决于配置)来处理。你将会看到随着并发连接数的增长以及更多的线程需要服务多个客户端时,那些开销有多消耗内存。Nginx跟Node.js都不是基于多线程模型的,因为线程跟进程都需要非常大的内存开销。他们都是单线程的,但是基于事件的。这种基于单线程的模型消除了为了处理很多请求而创建成百上千个线程或进程带来的开销。


Node.js为你的代码保持单线程的运行环境


它确实是基于单线程运行的,你无法编写任何代码来执行并发;例如执行一个"sleep"操作将阻塞整个服务器1秒钟。

while(new Date().getTime() < now + 1000) {
   // do nothing
}

因此,当代码运行的时候,node.js将不会响应来自客户端的其他请求,因为它只有一个线程来执行你的代码。或者,如果你有某些CPU密集型的操作,比如说,重置图片的尺寸,那也将阻塞所有其他的请求。


...然而,除了你的代码之外,其他的一切都是并发执行


在一个单独的请求里,没有办法可以使得代码并行执行。然而,所有的I/O都是基于时间的并且是异步的,所以接下来的代码将不会阻塞服务器:

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();
    }
);

如果你在一个请求中这么做,其他请求能够很好得被执行。


为什么这是更好的方式?什么时候我们需要从同步转向异步/并发执行?


采用同步执行是个不错的方式,因为它使得编码变得容易(对比线程而言,并发问题常常让你陷入万劫不复)。

在node.js中,你不需要去担心你的代码在后端会发生。你只需要在你做I/O操作的时候使用回调就可以了。你会得到保证:你的代码不会被中断,并且I/O操作也不会阻塞其他请求(因为没有了那些线程/进程需要花费的开销,比如在Apache中会发生的内存过高等)。

采用异步I/O也很好,因为I/O比那些执行其他操作更昂贵,我们应该做一些更有意义的事情而不是去等待I/O。


一个事件循环指的是——一个实体,它可以处理外部事件并且将它们转化为回调的执行。因此,I/O调用变成了node.js可以从一个请求切换到另外一个请求的“点”,你的代码保存了回调并返回控制权给node.js运行时环境。而回调在最终获得了数据之后被执行。

当然,在node.js内部,仍然是依靠线程和进程来进行数据访问、处理其他任务执行。然而,这些都没有明确地对你的代码暴露出来,所以你不需要额外担心内部如何处理I/O之间的交互。对比Apache的模型,它少去了很多线程以及线程开销,因为对每个连接来讲单独的线程不是必须的。仅仅是当你绝对需要让某个操作并发执行才会需要线程,但即便如此线程也是node.js自己管理的。

除了I/O调用之外,node.js期待所有的请求最好快速返回。比如,那些 CPU密集型的工作应该被隔离到另一个进程上去执行(通过与事件交互或者使用像 WebWorker一样的抽象)。这很明显意味着当你与事件交互的时候,如果没有另一个线程在后端(node.js运行时),那么你是无法并行化执行代码的。基本上,所有可以emit事件的对象(例如EventEmitter的实例)都支持基于事件的异步交互并且你也可以与“blocking code”交互(例如使用文件、sockets或者在node.js中是EventEmitter的子进程)。使用这种方案的话,就能够很好得 利用多核的优势了,可以看看:node-http-proxy。

内部实现

内部,node.js依赖于libev提供的事件循环,libeio是对于libev的补充,node.js使用池化的线程来提供对于异步I/O的支持。如果你想了解更多细节,你可以看一下libev的文档


如何在Node.js中实现异步


Tim Caswell在其PPT中描述了整个模式:

  • First-classfunction:例如我们将function作为数据传递,包裹他们以在需要的时候执行。
  • Function组装:就像你了解的关于异步函数或者闭包一样,在触发了I/O事件之后执行。
  • 回调计数器:对于基于事件的回调,你无法保证对于任何特殊的命令,I/O事件都会被执行。所以,一旦你需要多次查询来完成某个处理,通常你仅需要对任何的并发I/O操作进行计数,然后在你确实需要最后的结果的时候检查是否必要的操作都已全部完成(其中的一个例子是在事件回调中,通过对返回的数据库查询进行计数)。查询会被并发执行,并且I/O也对此提供支持(例如可以通过连接池的方式实现并发查询)。
  • 事件循环:上面已经提到过,你可以将blockingcode包裹进一个基于事件的抽象中去(比如通过运行一个子进程,然后当它执行完成之后再返回)。

真的非常简单!

再次申明原文出处:http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

另外,转载本文请著名“原文出处”,谢谢!




原文发布时间为:2013-09-28


本文作者:vinoYang


本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

目录
相关文章
|
3月前
|
存储 JavaScript 前端开发
深入理解JavaScript中的事件循环(Event Loop):机制与实现
【10月更文挑战第12天】深入理解JavaScript中的事件循环(Event Loop):机制与实现
148 3
|
4月前
|
JavaScript 前端开发 API
详解队列在前端的应用,深剖JS中的事件循环Eventloop,再了解微任务和宏任务
该文章详细讲解了队列数据结构在前端开发中的应用,并深入探讨了JavaScript的事件循环机制,区分了宏任务和微任务的执行顺序及其对前端性能的影响。
|
2月前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
|
2月前
|
JavaScript API 开发者
深入理解Node.js中的事件循环和异步编程
【10月更文挑战第41天】本文将通过浅显易懂的语言,带领读者探索Node.js背后的核心机制之一——事件循环。我们将从一个简单的故事开始,逐步揭示事件循环的奥秘,并通过实际代码示例展示如何在Node.js中利用这一特性进行高效的异步编程。无论你是初学者还是有经验的开发者,这篇文章都能让你对Node.js有更深刻的认识。
|
2月前
|
JavaScript 前端开发 开发者
JavaScript的事件循环
【10月更文挑战第27天】理解JavaScript的事件循环机制对于正确编写和理解JavaScript中的异步代码至关重要,它是JavaScript能够高效处理各种异步任务的关键所在。
46 1
|
3月前
|
前端开发 JavaScript
深入理解JavaScript中的事件循环(Event Loop):从原理到实践
【10月更文挑战第12天】 深入理解JavaScript中的事件循环(Event Loop):从原理到实践
52 1
|
3月前
|
Web App开发 JavaScript 前端开发
深入理解Node.js事件循环和异步编程模型
【10月更文挑战第9天】在JavaScript和Node.js中,事件循环和异步编程是实现高性能并发处理的基石。本文通过浅显易懂的语言和实际代码示例,带你一探究竟,了解事件循环的工作原理及其对Node.js异步编程的影响。从基础概念到实际应用,我们将一步步解锁Node.js背后的魔法,让你的后端开发技能更上一层楼!
|
3月前
|
设计模式 JavaScript API
Node.js 事件循环
10月更文挑战第3天
39 0
Node.js 事件循环
|
3月前
|
JavaScript 调度 数据库
深入浅出:Node.js中的异步编程与事件循环
【9月更文挑战第30天】在Node.js的世界里,理解异步编程和事件循环是掌握其核心的关键。本文将通过浅显易懂的语言和实际代码示例,带你探索Node.js如何处理并发请求,以及它是如何在幕后巧妙地调度任务的。我们将一起了解事件循环的各个阶段,并学会如何编写高效的异步代码,让你的应用程序运行得更加流畅。
73 10
|
3月前
|
JavaScript 前端开发 开发者
深入理解Node.js中的事件循环和异步编程
【9月更文挑战第31天】本文旨在揭示Node.js背后的强大动力——事件循环机制,并探讨其如何支撑起整个异步编程模型。我们将深入浅出地分析事件循环的工作原理,以及它如何影响应用程序的性能和稳定性。通过直观的例子,我们会展示如何在实际应用中利用事件循环来构建高性能、响应迅速的应用。此外,我们还会讨论如何避免常见的陷阱,确保代码既优雅又高效。无论你是Node.js的新手还是经验丰富的开发者,本篇文章都将为你提供宝贵的洞察和实用技巧。
71 6

热门文章

最新文章