Fibers, Event Loop和Meteor

Fibers, Event Loop和Meteor

写在前面: 刚开始使用Meteor,在官方文档看到In Meteor, your server code runs in a single thread per request这句话.一开始由于出于对nodejs粗浅的理解(单线程)并无很理解,因此找了一些资料,这篇文章解释清晰到位,虽然已是旧文(2013年),不过仍是打算翻译一下以供学习,若是错漏但愿你们不吝指教.
原文出自 https://meteorhacks.com/fibers-eventloop-and-meteor/html

Meteor用Fibers来实现许多重要的特性.事实上,Meteor的流行极可能是得益于使用了Fibers,虽然在深度了解Meteor以前可能不会意识到这件事.node

要了解Fibers是如何起效以及如何与Meteor进行关联仍是有些困难的.但一旦了解清楚了,会有助于咱们对Meteor内部工做原理有更加清晰的理解.git

做者注: Fibers原来并不在Pro Meteor topic讨论列表里,不过由于有人问到了,因此我决定写这篇文章,让咱们开始吧!github

Event Loop(事件循环)和Node.js

Meteor是基于Node.js的,因此咱们不能忘记Node.js的Event Loop(事件循环).虽然Node.js运行在单线程上,可是感谢事件循环以及事件驱动模式,I/O操做(主要是网络请求及硬盘读写)不会阻塞程序的执行.取而代之是提供一个回调函数在I/O操做结束后以供调用,而后再继续运行程序.npm

下面是两个伪代码例子,表示两个不一样的任务api

// Call functions.
fetchTwitterFollowers('arunoda');
createThumbnail('/tmp/files/arunoda.png', '/opt/data/arunoda.thumb.png');

// Define functions.
function fetchTwitterFollowers(username) {
  TwitterAPI.getProfile(username, function(){
    Model.setFollowers(profile.username, profile.followers, function() {
      console.log('profile saved!');
    });
  });
}

function createThumbnail(imageLocation, newLocation) {
  File.getFile(imageLocation, function(err, fileData) {
    var newImage = ImageModule.resize(fileData);
    File.saveFile(newLocation, function() {
      console.log('image saved');
    });
  });
}

如今让咱们来看看上面两个方法执行时的时序网络

time flow

被标注为绿色的是fetchTwitterFollowers任务,而被标注成橙色的则是createThumbnail.深色表明CPU时间,浅色表明I/O时间.
蓝色条表示任务队列的等待时间,红色条则是空转时间(CPU空闲)app

观察

上面的图展现了一些有趣的信息.异步

  • 任务的执行顺序不定(译者注: 表达的应该是回调的执行),I/O操做耗费的时间也不定以及它们不会阻塞其余程序的执行.上例能够看到,ImageModule.resize不须要等待Twitter.getProfile才执行.async

  • CPU被占用的确会阻塞其它任务执行.在上图中间区域,你能看到那条蓝色条表明尽管TwitterAPI.getProfileI/O操做已经完成了但依然不能开始执行Model.setFollowers.这是由于ImageModule.resize已经占用了CPU,因此阻塞了事件循环.就如前面提到的同样,Node.js是运行在单线程上的.这也是为何Node.js不适用于一些CPU密集型场景如图像处理和视频编码.

你也能看到有三个红色条指明了CPU空闲事件.若是咱们的例子还有其余任务的话,就用占用这些时间去执行.

Fibers

如今你了解事件循环是怎么工做,以及其高效率的缘由所在.但依然不能忽视问题: 回调函数.回调函数(或说回调模式)使得Node.js的代码难以推理(或被描述为回调沼泽).错误处理以及回调嵌套让代码变得难以书写,它们的存在致使代码更难维护以及扩展.这也是为何Node.js那么难学(以及难用)

幸运的是,已经有几种技术可用于攻克这个难题.如Fibers, Promises, 基于Generator的协程等等.

Meteor底层使用了Fibers,在这基础上封装了上层的APIs.在咱们更深刻了解以前,让咱们来看看Fibers是如何工做的.

time flow2

Fibers提供了一层事件循环的抽象,容许咱们按顺序的执行任务(或方法).让咱们能够摆脱回调模式来书写异步代码.咱们取得两个模式的精华-异步的高效率以及同步模式思考书写的代码.在这以后是由Fibers帮咱们处理事件循环的.

若是运用恰当Fibers将很是的强有力(Meteor就用得很是好).并且,使用Fibers形成的开销也是微乎其微的.

Meteor是如何使用Fibers的?

Meteor在其APIs上对Fibers进行了抽象,让咱们能够避免回调模式.并且最好的是你在书写避免回调模式的代码时甚至都没有察觉在使用Fibers,它就如此起效了.

Meteor为每个客户端的请求(DDP请求)建立一个Fiber.默认的,对于每个客户端Meteor每次只会处理一个请求,意味着每次只会为每一个客户端生成一个Fiber.可是这是能够进行改动的.

Fibers是Meteor如此受欢迎的理由之一.由于它容许咱们的Node.js应用脱离回调模式,这会吸引许多讨厌回调模式的开发人员.

如何在Meteor中使用异步方法

Meteor的API不能100%的知足咱们的需求,有时候咱们须要使用npm模块来处理事情.在不使用回调的状况下该怎么作呢?

举个例子,假设你须要使用Github的npm模块去请求用户的资料.而这个过程须要在一个Meteor的Method里面完成,最后咱们须要把这个资料从这个Method中返回出去.好的,让咱们尝试来实现这个需求

var GithubAPI = Meteor.require('github');
var ghapi = new GithubAPI({version: "3.0.0"});

Meteor.methods({
  getProfile: function(username) {
    ghapi.user.getFrom({user: username}, function(err, profile) {
      // How to return?
    });

    // We need to return the profile from here.
  }
});

咱们不能像上面使用回调.没有办法在回调中把用户资料返回出去,由于Meteor的Method不会等待回调再执行.如今咱们须要学习怎么使用Fibers来处理这种状况?仍是说有更好的选择?

Meteor已经考虑到这种状况而且给咱们提供了简单的API来处理.这个还没出如今文档中(译者注: 这篇是老文了,如今文档已经能查到相应的说明.详见 http://docs.meteor.com/api/core.html#Meteor-wrapAsync,这里介绍下该如何使用.

做者注: meteor-npm同时也有一系列的async-utilities搭配npm模块工做.

function getUserProfile(req, callback) {
  ghapi.user.getFrom(req, callback);
}
var wrappedGetProfile = Meteor._wrapAsync(getUserProfile);

Meteor.methods({
  getProfile: function(username) {
    return wrappedGetProfile({user: username});
  }
});

上面的代码很是好理解,咱们用一个方法包裹住ghapi.user.get,而后用Meteor._wrapAsync调用该方法去通知Fibers.接着咱们就能在其余使用方法和Meteor的APIs中使用上面的wrappedGetProfile来获取用户信息了.

若是你知道bind的话,你能够用下面的代码来达到一样的效果

var wrappedGetProfile = Meteor._wrapAsync(ghapi.user.getFrom.bind(ghapi.user));

Finally

如今你对事件循环,Fibers以及Meteor是如何使用Fibers都有个更好的了解了.同时你知道该若是经过Meteor._wrapAsync来使用异步方法.是时候来应用这些知识来加强你的应用了.

额外补充

若是你在但愿学习更多有关Fibers等方面的技术,请查阅下面列出的出自EventedMind很是好的视频.

相关文章
相关标签/搜索