理解Node.Js事件循環,寫出高性能代碼。

Node.Js事件循環

文章首發於個人Githubnode

事件循環是什麼

事件循環執行各階段解析

首先讓咱們看看事件循環是如何設計的,作了什麼,才能更好的理解它。上圖(來源Node官網)解釋了事件循環的6個階段及執行順序。下面咱們開始探索各階段都作了什麼?git

  • timers: 此階段執行由setTimeout()和setInterval()註冊的callback。
  • pending callbacks: 此階段執行某些系統操做(如TCP錯誤類型)的回調。例如,若是嘗試鏈接時TCP套接字收到ECONNREFUSED,則錯誤回調在這個階段執行。
  • idle, prepare: 只在內部使用(忽略)
  • pool: 檢索新的IO事件,執行相關IO的callback,node會在適當的時候阻塞。
  • check: 此階段執行由setImmediate() 註冊的callbacks
  • close callbacks: 該階段執行close 類型的callback,好比 socket.on('close', ...)。

每一個階段執行本身獨立的FIFO隊列裏面註冊的callback。每種類型的回調會註冊到各自的階段中。github

簡單的說:JS 代碼中的回調最終會到event loop 中註冊爲事件,event loop 把任務分配給 OS | thread pool (如今操做系統都提供的異步的接口,會優先選擇此選項),最後 event loop 輪詢OS任務完成的事件,取出結果,而後去event queue 中執行對於的callback。後端

事件循環的理解誤區

  • 一個主線程執行用戶代碼,另一個線程執行事件循環。每個異步操做發生時,主線程把工做交給事件循環線程,一旦工做完成,事件循環線程將會通知主線程執行回調。

事件循環的真實狀況

  • 僅僅只有一個線程來執行用戶代碼和事件循環(即事件執行循環的線程來執行用戶代碼),其實Node.Js中的每一個用戶代碼都是在事件循環中被執行的。
  • 經過下面案例來證實現實的運行狀況:
    const fs = require('fs');
    
    fs.readFile('testFile1.txt', function (err, data) {
        console.log('data read of testFile0.txt');
    
        // 模擬CPU密集型任務
        computing()
    });
    
    fs.readFile('testFile2.txt', function (err, data) {
        console.log('data read of testFile1.txt');
    });
    
    // CPU密集型任務
    function computing() {
        for (let i = 0; i < 10000; i++)//
        {
            for (let j = 0; j < 10000; j++) {
                for (let k = 0; k < 100; k++) { }
            }
        }
    }
    // 輸出結果:
    // 11:55:30 AM  data read of testFile0.txt
    // 11:55:56 AM  data read of testFile1.txt
    複製代碼
  • 經過運行結果,發現第二個回調比第一個回調延時了26秒。
  • 那是由於第二個回調函數在第一個回調後面執行,由於computing函數佔用的CPU26秒,阻斷了事件循環的進行,所以它延時了。
  • 也說明他們共用一個線程,回調函數一個一個(在事件循環中)被順序執行的。

libuv thread pool

  • libuv 默認會建立一個線程池,包含4個線程。用於處理異步任務。可是如今的操做系統已經爲I/O任務提供了異步的接口。因此回會優先使用異步接口,避免使用線程池,除非沒法避免的時候。

經過指標分析事件循環是否健康

  • Tick Frequency tick頻率(event loop 輪詢頻率)。
  • Tick Duration tick 間隔時間(event loop 兩次輪詢間的隔時間)。
  • Event Loop Tick 的頻率越高,說明集羣越健康。
  • 若是後端IO處理速度很慢,將會形成event loop 輪詢頻率下降。
  • Event Loop Latency 事件循環延時。
  • 事件循環延時若是明顯增高,說明但代碼中存在CPU密集型任務,須要優化。

總結

  • 事件循環的回調函數不會佔用太多 CPU 的計算能力. 由於一旦發生了上述狀況, 則意味着事件循環的執行速度會減慢, 事件得不到及時的處理。
  • Node.js 中同一個時刻只會有一個回調函數被執行。
相關文章
相關標籤/搜索