【译】理解node.js事件轮询

Node.js的第一个基本论点是I/O开销很大。

当前编程技术中等待I/O完成会浪费大量的时间。有几种方法可以处理这种性能上的影响:

  • 同步:每次处理一个请求,依次处理。优点:简单;缺点:任何一个请求都可以阻塞所有其他的请求。
  • Fork一个新进程:开一个新进程来处理每个请求。优点:容易;缺点:不能很好的扩展,成百上千个连接意味着成百上千个进程。fork()函数相当于Unix程序员的锤子,因为它很有用,每个问题看起来就像一个钉子,通常会被过度使用。(译者注:直译比较拗口,我理解的意思是,Unix程序员可以用fork()函数解决很多问题)
  • 线程:开一个新线程来处理每个请求。优点:容易,与使用fork相比,对内核更友好,因为线程通常开销较小;缺点:机器可能没有线程,线程编程可以非常复杂非常快,但是必须关注共享资源的权限问题。

第二个基本论点是每个连接开一个线程内存开销很大。

Apache是多线程的:它会为每个请求生成一个线程(或者进程,依据配置文件conf中的配置)。随着并发连接数的增加,你可以看到基本开销如何耗尽内存,需要更多的线程来服务同时在线的客户端。Nginx和Node.js不是多线程的,因为线程和进程会带来很大内存负担。它们是单线程,但是基于事件的。在一个线程中处理多个连接的开销比在成千上万个线程或进程中处理连接的开销小。

Node.js让你的代码保持单线程……

它真的是单个线程在运行:你不能有任何并行的代码操作;例如模拟一个"sleep"阻塞服务器一秒钟:

var now = new Date().getTime();
while(new Date().getTime() < now + 1000){}

当这段代码执行时,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();
    }
); 

如果你在一个请求中这么做的话,当数据库正在执行sleep时,其他的请求也可以被处理的很好。

为什么这是有好处的?什么时候我们可以从同步变成异步/并行执行?

同步执行有好处,因为可以简单的写代码(与多线程相比,并发性问题有导致WTFs的趋势)(译者注:WTF在此处是What The Fuck的缩写)。

在node.js中,你不应该担心后台发生了什么:当你做I/O操作时,只需要使用回调函数;可以保证你的代码不会被中断,I/O操作不会阻塞其他请求,也无需承担每个请求的线程/进程成本(例如Apache中的内存开销)。

异步I/O操作也有好处,因为I/O操作比大多数代码开销更大,我们应该做一些更好的事情而不仅仅是等待I/O操作。

事件轮询是"一种掌握和处理外部事件并且把它们转成回调调用的实体"。因此I/O调用是一些调用点,node.js可以在这些点从一个请求转换到另一个请求。在一次I/O调用时,你的代码保存回调并且把流程控制权返给node.js的运行环境。当数据可用时,回调方法会被调用。

当然,在后台还是有线程和进程进行数据库访问和流程执行的。但是你的代码不需要显示的接触这些,因此你不必担心它们,只需知道I/O交互就行了。例如从每个请求的角度来看,数据库或其他流程需要异步,因为这些线程的结果通过事件轮询返回给你的代码。与Apache模块相比,因为不需要为每个连接创建线程,可以减少线程和线程开销;只有当你确定有些事情需要并行处理,即使这样也通过node.js来处理这种管理。

除了I/O调用,Node.js希望所有的请求都可以快速的返回;例如CPU密集型任务应该被分离到另一个进程中,然后通过事件进行交互,或者通过使用一个抽象概念像WebWorkers。这(明显的)意味着你不能并行化你的代码,除非后台有另一个线程,你可以通过事件与之交互。基本上所有的能发出事件的对象(例如EventEmitter的实例)都支持异步事件交互,你可以用这种方式与阻塞代码交互,例如正在使用的文件,sockets或者子进程,所有这些都是node.js中的EventEmitters。多核可以使用这种方法;参见node-http-proxy。

内部实现

在内部,node.js依赖libev提供事件轮询,libev依赖libeio,libeio使用线程池来提供异步I/O。想要进一步学习事件,看下libev的文档

那么在node.js中如何做到异步?

  • 一等函数。例如我们把函数作为数据传递,改变它们,需要的时候会执行它们。
  • 复合函数。又称为匿名函数或闭包,在基于事件的I/O中发生某种事件时执行。

(译者注:即第一种是传递定义好的函数,第二种是传递匿名函数)

原文链接http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

时间: 2024-12-27 17:49:35

【译】理解node.js事件轮询的相关文章

node.js事件轮询(1)

事件轮询(引用) 事件轮询是node的核心内容.一个系统(或者说一个程序)中必须至少包含一个大的循环结构(我称之为"泵"),它是维持系统持续运行的前提.nodejs中一样包含这样的结构,我们叫它"事件轮询",它存在于主线程中,负责不停地调用开发者编写的代码.我们可以查看nodejs官方网站上对nodejs的说明: Node is similar in design to and influenced by systems like Ruby's Event Mach

理解Node.js的事件轮询

前言 总括 : 原文地址:理解Node.js的事件轮询 Node小应用:Node-sample 智者阅读群书.亦阅历人生 正文 Node.js的两个基本概念 Node.js的第一个基本概念就是I/O操作开销是巨大的: 所以,当前变成技术中最大的浪费来自于等待I/O操作的完毕.有几种方法能够解决性能的影响: 同步方式:按次序一个一个的处理请求.利:简单.弊:不论什么一个请求都能够堵塞其它全部请求. 开启新进程:每一个请求都开启一个新进程.利:简单:弊:大量的链接意味着大量的进程. 开启新线程:每一

node.js的事件轮询机制

借助libuv库实现的 概括事件轮询机制:分为六个阶段1.timers 定时器阶段计时和执行到点的定时器回调函数 2.pending callbacks某些系统操作(例如TCP错误类型) 3.idle,prepare 4.poll轮询阶段(轮询队列)如果轮询队列不为空,依次同步取出轮询队列中第一个回调函数,直到轮询队列为空或者达到系统最大限制如果轮询队列为空 如果之前设置过setImmediate函数,直接进入下一个check阶段,如果之前没有设置过setImmediate函数,在当前 poll

浅析libuv源码-node事件轮询解析(1)

好久没写东西了,过了一段咸鱼生活,无意中想起了脉脉上面一句话: 始终保持自己的竞争力.所以,继续开写! 一般的JavaScript源码看的已经没啥意思了,我也不会写什么xx入门新手教程,最终决定还是啃原来的硬骨头,从外层libuv => node => v8一步步实现原有的目标吧. libuv核心还是事件轮询,前几天从头到尾看了一遍官网的文档,对此有了一些更深的理解. (虽然现在开发用的mac,但是为了衔接前面的文章,所以代码仍旧以windows系统为基础,反正差别也不大) 首先看一眼官网给的

浅析libuv源码-node事件轮询解析(4)

这篇应该能结,简图如下. 上一篇讲到了uv__work_submit方法,接着写了. void uv__work_submit(uv_loop_t* loop, struct uv__work* w, enum uv__work_kind kind, void (*work)(struct uv__work* w), void (*done)(struct uv__work* w, int status)) { // 上篇主要讲的这里 初始化线程池等 uv_once(&once, init_on

JS中的异步以及事件轮询机制

一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步均是通过多线程实现的,没有循环队列一说,直接在子线程中完成相关的操作) JavaScript的单线程,与它的用途有关.作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM.这决定了它只能是单线程,否则会带来很复杂的同步问题.比如,假定JavaScript同时有两个线程,一个

JS高阶---事件循环模式(事件轮询)

大纲: 相关知识点: 主体: (1)模型原理 JS部分:初始化代码执行 WebAPIS:执行上下文对象(不是一个真的对象,而是一个抽象的虚拟对象,可以看做栈里的一个区域,包含很多对象) setTimeout:定时器管理模块(分线程) DOM(document):事件响应管理模块(分线程)callback queue:回调函数队列---→待执行JS中的stack初始化代码优先执行,当执行完毕后,才会执行回调代码 重点: 先执行初始化代码,执行完毕后才会循环遍历“”回调队列“”里的回调代码 (2)相

【转】【译】Node.js 是什么?

著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:贾厂长链接:https://www.zhihu.com/question/33578075/answer/56951771来源:知乎 如果你去年注意过技术方面的新闻,我敢说你至少看到node.js不下一两次.那么问题来了“node.js是什么?”.有些人没准会告诉你“这是一种通过JavaScript语言开发web服务端的东西”.如果这种晦涩解释还没把你搞晕,你没准会接着问:“为什么我们要用node.js?”,别人一般会告诉

理解 Node.js 里的 process.nextTick()

有很多人对Node.js里process.nextTick()的用法感到不理解,下面我们就来看一下process.nextTick()到底是什么,该如何使用. Node.js是单线程的,除了系统IO之外,在它的事件轮询过程中,同一时间只会处理一个事件.你可以把事件轮询想象成一个大的队列,在每个时间点上,系统只会处理一个事件.即使你的电脑有多个CPU核心,你也无法同时并行的处理多个事件.但也就是这种特性使得node.js适合处理I/O型的应用,不适合那种CPU运算型的应用.在每个I/O型的应用中,