深刻理解Node.js-背景瞭解

標籤: node 源碼 學習javascript


源碼: nianniuervue


node背景,瞭解一下

(1)體系架構

Node.js主要分爲四大部分,Node Standard Library,Node Bindings,V8,Libuv,架構圖以下:java

cmd-markdown-logo

  • Node Standard Library 是咱們天天都在用的標準庫,如Http, Buffer 模塊。
  • Node Bindings 是溝通JS 和 C++的橋樑,封裝V8和Libuv的細節,向上層提供基礎API服務。
  • 這一層是支撐 Node.js 運行的關鍵,由 C/C++ 實現。
    • V8 是Google開發的JavaScript引擎,提供JavaScript運行環境,能夠說它就是 Node.js 的發動機。
    • Libuv 是專門爲Node.js開發的一個封裝庫,提供跨平臺的異步I/O能力
    • C-ares:提供了異步處理 DNS 相關的能力。
    • http_parser、OpenSSL、zlib 等:提供包括 http 解析、SSL、數據壓縮等其餘的能力。

代碼結構

樹形結構查看,使用 tree 命令node

➜  nodejs git:(master) tree -L 1
.
├── AUTHORS
├── BSDmakefile
├── BUILDING.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── COLLABORATOR_GUIDE.md
├── CONTRIBUTING.md
├── GOVERNANCE.md
├── LICENSE
├── Makefile
├── README.md
├── ROADMAP.md
├── WORKING_GROUPS.md
├── android-configure
├── benchmark
├── common.gypi
├── config.gypi
├── config.mk
├── configure
├── deps
├── doc
├── icu_config.gypi
├── lib
├── node.gyp
├── out
├── src
├── test
├── tools
└── vcbuild.bat
複製代碼

進一步查看 deps目錄:python

➜  nodejs git:(master) tree deps -L 1
deps
├── cares
├── gtest
├── http_parser
├── npm
├── openssl
├── uv
├── v8
└── zlib
複製代碼

node.js核心依賴六個第三方模塊。其中核心模塊 http_parser, uv, v8這三個模塊在後續章節咱們會陸續展開。 gtest是C/C++單元測試框架。linux

(2)libuv

node.js最初開始於2009年,是一個可讓Javascript代碼離開瀏覽器的執行環境也能夠執行的項目。 node.js使用了Google的V8解析引擎和Marc Lehmann的libev。Node.js將事件驅動的I/O模型與適合該模型的編程語言(Javascript)融合在了一塊兒。隨着node.js的日益流行,node.js須要同時支持windows, 可是libev只能在Unix環境下運行。Windows 平臺上與kqueue(FreeBSD)或者(e)poll(Linux)等內核事件通知相應的機制是IOCP。libuv提供了一個跨平臺的抽象,由平臺決定使用libev或IOCP。在node-v0.9.0版本中,libuv移除了libev的內容。android

libuv是一個高性能的,事件驅動的I/O庫,而且提供了跨平臺(如windows, linux)的API。 隨着libuv的日益成熟,它成爲了擁有卓越性能的系統編程庫。除了node.js之外,包括Mozilla的Rust編程語言,和許多的語言都開始使用libuv。 libuv的官網:docs.libuv.org/en/v1.x/,英文…git

(3)V8 concept[概念]

架構圖 github

cmd-markdown-logo

如今 JS 引擎的執行過程大體是:源代碼 --->抽象語法樹 --->字節碼 --->JIT--->本地代碼。一段代碼的抽象語法樹示例以下:web

function demo(name) {
    console.log(name);
}
複製代碼

V8 更加直接的將抽象語法樹經過 JIT 技術轉換成本地代碼,放棄了在字節碼階段能夠進行的一些性能優化,但保證了執行速度。 在 V8 生成本地代碼後,也會經過 Profiler 採集一些信息,來優化本地代碼。雖然,少了生成字節碼這一階段的性能優化, 但極大減小了轉換時間。

JIT就是即時編譯器,能夠根據字節碼的使用頻率對經常使用的字節碼生成本地機器指令(運行時),而且保存下來

PS:Tuborfan 將逐步取代 Crankshaft

隨着Web相關技術的發展,JavaScript所要承擔的工做也愈來愈多,早就超越了「表單驗證」的範疇,這就更須要快速的解析和執行JavaScript腳本。V8引擎就是爲解決這一問題而生,在node中也是採用該引擎來解析JavaScript。

Node,開始學習!

1. Node可以解決什麼問題?

應用場景:Node的首要目標是提供一種簡單的,用於建立高性能服務器的開發工具 Web服務器的瓶頸在於併發的用戶量,對比Java和Php的實現方式

Node在處理高併發 , I/O密集場景有明顯的性能優點
  • I/O (input/output 文件的輸入輸出)
  • 高併發,是指在同一時間併發訪問服務器
  • I/O密集指的是文件操做、網絡操做、數據庫,相對的有CPU密集,CPU密集指的是邏輯處理運算、壓縮、解壓、加密、解密

2. Node是什麼?

  • Node.js 是一個基於 Chrome V8引擎的 JavaScript 運行環境,讓JavaScript的執行效率與低端的C語言的相近的執行效率。
  • Node.js 使用了一個事件驅動非阻塞式 I/O 的模型,使其輕量又高效。
  • Node.js 的包管理器npm,是全球最大的開源庫生態系統。

3. 進程與線程

進程是操做系統分配資源和調度任務的基本單位,線程是創建在進程上的一次程序運行單位,一個進程上能夠有多個線程。

爲何JavaScript是單線程?
  • 這是由 Javascript 這門腳本語言的用途決定的。
  • Web Worker並無改變 JavaScript 單線程的本質。
// websocket
    let worker  = new Worker('./1.worker.js');
    worker.postMessage(10000);
    worker.onmessage = function (e) {
      console.log(e.data);
    }
    console.log('main thread')
複製代碼
3.1 談談瀏覽器

cmd-markdown-logo

  • 用戶界面-包括地址欄、前進/後退按鈕、書籤菜單等
  • 瀏覽器引擎-在用戶界面和呈現引擎之間傳送指令(瀏覽器的主進程)
  • 渲染引擎,也被稱爲瀏覽器內核(瀏覽器渲染進程)
  • 一個插件對應一個進程(第三方插件進程)
  • GPU提升網頁瀏覽的體驗(GPU進程)

因而可知瀏覽器是多進程的,而且從咱們的角度來看咱們更加關心瀏覽器渲染引擎

3.2 渲染引擎

渲染引擎內部是多線程的,內部包含兩個最爲重要的線程ui線程和js線程。這裏要特別注意ui線程和js線程是互斥的,由於JS運行結果會影響到ui線程的結果。ui更新會被保存在隊列中等到js線程空閒時當即被執行.


3.3 js單線程

javascript在最初設計時設計成了單線程,爲何不是多線程呢?若是多個線程同時操做DOM那豈不會很混亂?這裏所謂的單線程指的是主線程是單線程的(node其實也是多線程的),因此在Node中主線程依舊是單線程的。

  • js線程和ui線程 是共享線程
  • 不能兩個線程,操做同一個DOM

什麼是多線程?
  • 同步
  • 每次請求都會產生一個線程,可能會遇到同時操做一個文件,會給文件加鎖
  • 能夠開一個工做線程,可是歸主線程所管理
  • 須要切換上下文執行
  • 運用線程池能夠優化多線程

什麼是單線程?
  • 異步
  • 不會佔用過多內存,而且不須要在切換執行上下文

什麼是進程?
  • 進程是系統分配任務和調度任務的基本單位
  • 多進程 可能會遇到同時操做一個文件會給文件加鎖
3.4 其餘線程
  • 瀏覽器事件觸發線程(用來控制事件循環,存放setTimeout、瀏覽器事件、ajax的回調函數)
  • 定時觸發器線程(setTimeout定時器所在線程)
  • 異步HTTP請求線程(ajax請求線程)

單線程不須要管鎖的問題,這裏簡單說下鎖的概念。例如你們都要去上廁所,廁所就一個,至關於全部人都要訪問同一個資源。那麼先進去的就要上鎖。而對於node來講。就一我的去廁所,因此免除了鎖的問題!


3.5. 瀏覽器中的Event Loop

主線程從任務隊列中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)

cmd-markdown-logo

  1. V8引擎解析JavaScript腳本。
  2. 解析後的代碼,調用Node API。
  3. libuv庫負責Node API的執行。它將不一樣的任務分配給不一樣的線程,造成一個Event Loop(事件循環),以異步的方式將任務的執行結果返回給V8引擎。
  4. V8引擎再將結果返回給用戶。
queue stack
  • queue隊列: 先進先出 push shift
  • stack棧: 函數執行

3.6 Node.js的Event Loop

cmd-markdown-logo


3.7. 同步與異步

同步和異步關注的是消息通知機制

  • 同步就是發出調用後,沒有獲得結果以前,該調用不返回,一旦調用返回,就獲得返回值了。 簡而言之就是調用者主動等待這個調用的結果。
  • 而異步則相反,調用者在發出調用後這個調用就直接返回了,因此沒有返回結果。換句話說當一個異步過程調用發出後,調用者不會馬上獲得結果,而是調用發出後,被調用者經過狀態、通知或回調函數處理這個調用。

補充知識:宏任務 && 微任務(vue $nextTick) 異步方法

// 宏任務 setTimeout
// 同步代碼執行後纔會執行異步
// 根據時間排序,當時間到達後把對應的回調放到隊列
    setTimeout(() => {
      console.log(1);
      setTimeout(() => {
        console.log(4);
      }, 1000);
    }, 1000);
    setTimeout(() => {
      console.log(2);
    }, 2000);
    setTimeout(() => {
      console.log(3);
    }, 3000);
複製代碼
// 宏任務 setImmedate
    // setImmedate只兼容ie,默認是低於 setTimeout
    setImmediate(function () {
      console.log('setImmediate')
    })
    setTimeout(function () {
      console.log('timeout')
    }, 4);
    console.log(1);
複製代碼
// 宏任務 MessageChannel
    let messageChannel = new MessageChannel();
    let prot2 = messageChannel.port2;
    // postMessage是異步執行的,要等待同步都執行完後纔會被調用
    messageChannel.port1.postMessage('111');
    console.log(1);
    prot2.onmessage = function (e) {
      console.log(e.data);
    }
    console.log(2);
複製代碼
// 宏任務 MutationObserver廢棄了,兼容/性能有問題
     let observe = new MutationObserver(function () {
        alert('已經dom更新好了')
     });
     observe.observe(app,{childList:true});
     for(let i = 0;i<1000;i++){
        app.appendChild(document.createElement('span'));
    }
複製代碼

3.8. 阻塞與非阻塞I/O

阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態.

  • 阻塞調用是指調用結果返回以前,當前線程會被掛起。調用線程只有在獲得結果以後纔會返回。
  • 非阻塞調用指在不能馬上獲得結果以前,該調用不會阻塞當前線程。

3.9. 組合

同步異步取決於被調用者,阻塞非阻塞取決於調用者

  • 同步阻塞
  • 異步阻塞
  • 同步非阻塞
  • 異步非阻塞

4. 什麼場合下應該考慮使用Node框架?

當應用程序須要處理大量併發的輸入輸出,而在向客戶端響應以前,應用程序並不須要進行很是複雜的處理。

  • 聊天服務器
  • 電子商務網站
相關文章
相關標籤/搜索