tornado異步編程與node.js異步編程

tornado是由FaceBook開源的一個異步python框架,node.js是由Joyent資助的開源項目,致力於提供一套編寫高性能併發Web應用的JavaScript框架。這篇博客就簡單談一談兩種異步框架在Linux下的異同,若是有錯誤或者表達不恰當的地方歡迎各路大神指正。html

在Linux下,node.js靠libev和libeio配合使用來實現異步I/O。libev是一個事件驅動庫,基於poll/epoll提供的I/O複用方式,主要用於事件驅動的網絡編程。libeio是異步版的POSIX API,主要負責文件I/O操做,他僅僅依賴於pthread,能夠和libev配合使用。libev和libeio都是經過c和c++來實現的,因此node.js的底層是c和c++。node

tornado的異步主要是經過 ioloop和iostream這2個模塊來實現的。ioloop是tornado本身實現的事件驅動模型,和libev同樣,也是對poll/epoll的封裝,他使用_handlers保存(fd,handler)的映射關係,_events保存就緒的fd以及對應的events事件,tornado提供了add_handler, update_handler、remove_handler這3個方法對handler進行操做。而後調用start函數就能夠開始對全部的fd進行輪詢,而且調用對應的handler。而iostream是對ioloop的一層封裝,主要實現socket的異步調用,主要由如下的3個方法實現讀寫操做read_until, read_bytes, write。python

從異步的實現方式上面看,在Linux下2者底層都是對poll/epoll的封裝,只是一個底層由c實現,另外一個徹底由python實現。ios

從代碼風格上講我以爲tornado比node.js有優點,node.js是函數式編程,異步操做處理獲得的結果只能經過回調函數進行處理,假如說咱們須要進行多級的函數調用,而且結果互相依賴,那麼這個時候就只能在回調函數裏面嵌套回調函數,這樣的代碼可讀性和可維護性都不高,好比:c++

當咱們須要讀取一個文件的時候,咱們會執行如下語句:web

fs.readFile('/path', function (err, data) {
  if (err) throw err;
  console.log(data);
});

當咱們須要讀取一個文件的內容,而且根據這個文件的內容讀寫另一個文件的時候,咱們就須要執行如下語句:編程

fs.readFile('/path', function (err, data) {
  if (err) throw err;
  fs.readFile(data, function (err, data2) {
    if (err) throw err;
    // 在這裏處理data2的數據
  });
});

這個地方咱們就在回調裏面嵌套了一層回調,這裏僅僅是2層,假如回調嵌套層數不少的話代碼就不容易讀懂,看起來結構就很亂。後端

tornado爲了解決這個問題提供了一種使用同步的方式編碼,而且使用異步的方式執行的方法。解決方式就是使用python的yield關鍵字和gen模塊。websocket

gen模塊能夠把一個函數調用裏面的回調函數的參數當成返回值賦給變量,若是有多個參數就返回一個tuple。可是這個並無解決異步調用時間差的問題,若是直接使用這個方法的話獲得的值是None,由於回調函數剛剛加入輪詢隊列,尚未被執行,這個時候gen就嘗試讀取參數而且返回,這個時候獲得的就是None。這個時候就須要yield關鍵字,這個關鍵字能夠把一個函數變成一個生成器,簡單的來講就是調用一個帶有yield關鍵字的函數的時候,函數會先順序執行到yield這一句,而後函數被掛起,當這個函數再一次被執行的時候,函數會從yield這一句接着往下執行。若是yield和gen搭配使用的話,函數會先執行到yield這一句,而後把調用的函數加入輪詢隊列,而後函數被掛起,等待輪詢裏面的函數執行完畢返回的時候函數再次被調用,經過gen取出回調函數裏面的參數而且賦值給變量,函數接着往下執行。例如:網絡

若是不使用gen模塊的話異步調用須要這樣寫:

class AsyncHandler(RequestHandler):
    @asynchronous
    def get(self):
        http_client = AsyncHTTPClient()
        http_client.fetch("http://example.com",
                          callback=self.on_fetch)

    def on_fetch(self, response):
        do_something_with_response(response)
        self.render("template.html")

on_fetch就是回調函數

若是使用gen模塊代碼能夠這樣寫:

class GenAsyncHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch("http://example.com")
        do_something_with_response(response)
        self.render("template.html")

代碼中使用了@gen.coroutine修飾器,而且經過yield關鍵字將異步的代碼變成同步的寫法。這樣代碼的邏輯就很符合大多數人的編程思惟,大大加強了代碼的可讀性。

總的來講node.js和tornado都是不錯的異步編程工具,對於高併發都有很強的處理能力,node.js更適合全棧式的先後端分離的開發,tornado則是一個輕量級的python框架,他的優點更多的是在對websocket和長鏈接的支持,知乎就是使用tornado開發的。

相關文章
相關標籤/搜索