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很是好的視頻.

相關文章
相關標籤/搜索