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/

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

  1. node.js事件轮询(1)

    事件轮询(引用) 事件轮询是node的核心内容.一个系统(或者说一个程序)中必须至少包含一个大的循环结构(我称之为"泵"),它是维持系统持续运行的前提.nodejs中一样包含这样的 ...

  2. [译] 所有你需要知道的关于完全理解 Node.js 事件循环及其度量

    原文地址:All you need to know to really understand the Node.js Event Loop and its Metrics 原文作者:Daniel Kh ...

  3. js事件轮询机制

    console.log(1) setTimeout(function(){ console.log(2) },0); console.log(3) 毫无疑问:运行结果是1 3 2 也就是说:setTi ...

  4. Node.js事件的正确使用方法

    前言 事件驱动的编程变得流行之前,在程序内部进行通信的标准方法非常简单:如果一个组件想要向另外一个发送消息,只是显式地调用了那个组件上的方法.但是在 react 中用的却是事件驱动而不是调用. 事件的 ...

  5. 理解Node.js的事件轮询

    前言 总括 : 原文地址:理解Node.js的事件轮询 Node小应用:Node-sample 智者阅读群书,亦阅历人生 正文 Node.js的两个基本概念 Node.js的第一个基本概念就是I/O操 ...

  6. 对Node.JS的事件轮询(Event Loop)的理解

    title: Node.JS的事件轮询(event loop)的理解 categories: 理解 tags: Node JS 机制 当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 ...

  7. Node.js的异步IO和事件轮询

     想象一下,以前我们在写程序时, 如果程序在I/O上阻塞了,当有更多请求过来时,服务器会怎么处理呢?在这种情景中通常会用多线程的方式.一种常见的实现是给每个连接分配一个线程,并为那些连接设置一个线程池 ...

  8. Node.js的事件轮询Event Loop原理

    Node.js的事件轮询Event Loop原理解释 事件轮询主要是针对事件队列进行轮询,事件生产者将事件排队放入队列中,队列另外一端有一个线程称为事件消费者会不断查询队列中是否有事件,如果有事件,就 ...

  9. node.js中的事件轮询Event Loop

    任务队列/事件队列 "任务队列"是一个事件的队列,IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈" ...

随机推荐

  1. K-means中的K值选择

    关于如何选择Kmeans等聚类算法中的聚类中心个数,主要有以下方法(译自维基): 1. 最简单的方法:K≍sqrt(N/2) 2. 拐点法:把聚类结果的F-test值(类间Variance和全局Var ...

  2. jsonk可以传递boolean

    JSON 布尔值可以是 true 或者 false.如: { "flag":true } 或者{“flag”,"true"}

  3. FZU 2092 收集水晶(记忆化搜索)

    Problem 2092 收集水晶 Accept: 101 Submit: 439 Time Limit: 5000 mSec Memory Limit : 32768 KB Problem Desc ...

  4. POJ_3264_Balanced Lineup

    Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 57259   Accepted: 26831 ...

  5. LINUX系统中安装ORACLE11g的安装文档,含部分问题解答

    1.无法使用命令 /usr/bin/xdpyinfo 自动检查显示器颜色 在linux as5下安装oracle11g,环境都配置好了!等运行./runInstaller的时候无法弹出安装的图形界面我 ...

  6. kubernetes实战(二十):k8s一键部署高可用Prometheus并实现邮件告警

    1.基本概念 本次部署使用的是CoreOS的prometheus-operator. 本次部署包含监控etcd集群. 本次部署适用于二进制和kubeadm安装方式. 本次部署适用于k8s v1.10版 ...

  7. SQL SERVER 聚集索引 非聚集索引 区别

    转自http://blog.csdn.net/single_wolf_wolf/article/details/52915862 一.理解索引的结构 索引在数据库中的作用类似于目录在书籍中的作用,用来 ...

  8. 002-nginx-在 nginx 反向代理中使用域名,配置动态域名解析

    一.概述 代理(proxy),即中间人,它代替客户端发送请求给服务器,收到响应后再转给客户端.通常意义上的代理是从用户的角度讲的,用户通过某个代理可以访问多个网站,这个代理是靠近用户的,比如某些公司可 ...

  9. ppt插入声音

    1:点击插入>音频>文件中的音频 2:插入成功后,会出现一个声音的图表 3:对播放格式进行设置,设置循环播放等. 4:双击对声音进行编辑 ,会出现右边的各个组件, 5:点击下拉框>效 ...

  10. elasticsearch 6.0在Ubuntu下的安装

    1:直接下载 elasticsearch 6.0  zip文件  https://www.elastic.co/downloads/past-releases 2:解压:进入到解压后的bin目录,执行 ...