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. Docker 安装Centos,Tomcat,Jdk等相关的自定义(Dockerfile)镜像

    一.安装Centos镜像 这里Centos 安装 国内daocloud网站提供的官方镜像 docker pull daocloud.io/library/centos:latest 利用docker  ...

  2. 9.20Ajax知识sweetalet

    2018-9-20 14:19:55 2018-9-20 21:33:05 周末可以帮我图书商城再次优化一下!! 加入 Ajax请求,,再加上 sweetAlert  甜蜜对话框插件! 要是再加上模态 ...

  3. 不错的Spring学习笔记(转)

    Spring学习笔记(1)----简单的实例 ---------------------------------   首先需要准备Spring包,可从官方网站上下载.   下载解压后,必须的两个包是s ...

  4. SPOJ BALNUM - Balanced Numbers - [数位DP][状态压缩]

    题目链接:http://www.spoj.com/problems/BALNUM/en/ Time limit: 0.123s Source limit: 50000B Memory limit: 1 ...

  5. hive学习-测试数据

    测试数据: ---------------------------------------------MySQL-------------------------------------------- ...

  6. 单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。

    https://github.com/alibaba/p3c/blob/master/阿里巴巴Java开发手册(详尽版).pdf 单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表 ...

  7. Nginx Upstream timed out (110: Connection timed out)

    Nginx Upstream timed out (110: Connection timed out) – 运维生存时间 http://www.ttlsa.com/nginx/nginx-upstr ...

  8. UDR rsync

    1. SOCK_DGRAM  UDP packets SOCK_STREAM   TCP 不同的协议下的 套接字 数据包 面向数据的 面向连接的 套接字 2. 数据 UDP 文件 TCP https: ...

  9. maven package install deploy区别

    package 命令完成了项目编译.单元测试.打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库install 命令完成了项目编译.单元测 ...

  10. find a way to escape--hdu1593

    题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=1593 找到二者角速度相等时水中人的R,在此之前二者保持在一条直线上,之后水中的人沿直线到岸边S点匀 ...