Deno 裡面有一句描述:"Aims to be browser compatible",可以看到 Deno 的目標是相容瀏覽器。那麼這裡的相容瀏覽器到底如何是什麼意思呢?
我簡單談談我的理解吧。
首先這裡的相容性肯定不是 Deno 直接在瀏覽器端執行。因為 Deno 是一個和瀏覽器平級的 Runtime。
很多人還有誤解以為相容瀏覽器指的是 Deno 會提供“類似 Node.js 裡的 UMD 寫法”。首先我們明確一點,這裡的相容不是單指語法層面的相容,並不是說要相容 ES3 ES5。所以不要產生這種誤解,以為可以通過 babel 相容 Node.js 和 Deno。
在 Deno 的 Roadmap 裡面作者就已經寫到了:
Deno does not aim to be API compatible with Node in any respect. Deno will export a single flat namespace "deno" under which all core functions are defined. We leave it up to users to wrap Deno's namespace to provide some compatibility with Node.
這裡的相容,我的理解是相容瀏覽器的 API 和生態。(坐等被打臉)
之前有個 issue discussion: struct the browser-compatible APIs #82 討論這個問題,在 issue 中列舉了一些想要相容的瀏覽器 API:
- High level
- Console ✓
- URL ✓
- File/FileList/FileReader/Blob
- XMLHttpRequest/Fetch
- WebSocket
- URLSearchParams
- Middle level
- AudioContext/AudioBuffer
- Canvas
討論中還包括 WebGL 設定 GPU 的支援。我們可以隱約猜到 Deno 的一個目標就是讓瀏覽器中的程式碼可以直接執行在 Deno 上面。
我的觀點依然是:Deno 不是下一代 Node.js。(再次坐等被打臉)
Deno 是一個“A secure TypeScript runtime on V8”。一個安全的基於 V8 的 TypeScript 執行時,這個怎麼理解呢。
瀏覽器可以認為是安全的 JavaScript 執行時,所有的 JavaScript 程式碼都是在沙盒(Sandbox)裡面執行。瀏覽器雖然安裝在你的電腦上,但是瀏覽器裡面執行的 JavaScript 程式碼可以來自世界各地,換言之瀏覽器裡面執行的都是不受信程式碼,如何保證瀏覽器的 JavaScript 程式碼不破壞你的電腦系統,這是瀏覽器安全機制需要解決的一個問題。而 Node.js 則不是,和任何的 Web 伺服器一樣,Node.js 執行的是受信程式碼。
之前 V8 出現了關於逃逸分析(Escape Analysis)的安全漏洞,Chrome 採取了緊急措施,在下一個版本中刪掉了逃逸分析相關的功能,相比之下,Node.js 則沒有受到影響,因為 Node.js 執行的程式碼本來就是受信的。
從這個角度講,Deno 和瀏覽器的定位很像。
因為我們可以看到,相容伺服器端生態和相容瀏覽器端生態的一個區別:
- 瀏覽器裡面執行的都是不受信程式碼
- 伺服器執行的是受信程式碼
我們再換一個角度聊聊伺服器生態。
很多人還有一個誤解,就是覺得上面提到的那些 API,對於 Node.js 也完全可以相容,比如 Console、URL、XMLHttpRequest/Fetch 等,很多 API 都已經被 Node.js 實現了。
當 Node.js 被開發出來的時候,類似 File、URL、Buffer 這類的 API 瀏覽器端都還沒有,但由於 Node.js 定位為伺服器端執行平臺,因此 Node.js 參考的是其他 Web 伺服器或者伺服器程式語言。例如檔案系統(File System),實現了一系列 POSIX(Portable Operating System Interface,可移植作業系統介面) 相容的函式和功能。
還有一點需要注意的是 Node.js 和瀏覽器的趨同化,例如 Node.js 7 加入的 URL API,而當今的主流瀏覽器也都提供了這個 API,這是因為 Node.js 也使用了 WHATWG URL 標準。
WHATWG 的全稱是 Web Hypertext Application Technology Working Group,網頁超文字應用技術工作小組。這是一個相當“有重量”的組織,當 W3C 決定放棄 HTML 打算將未來的重點放在 XHTML 2.0 上時,WHATWG 堅決擁護 HTML,並制定了下一代 HTML 計劃。最終 WHATWG 說服了 W3C 並與其一起釋出了 HTML5。
如今 Web 的飛速發展我們要感謝這些組織的努力。
那麼現在就有一個疑問了:如果 Node.js、瀏覽器、Deno 都使用這些標準後,那麼他們是不是就會變得一樣,Deno 會不會取代 Node.js,成為下一代 Node.js?或者 Deno 變成一個和 Node.js 一樣但是比 Node.js 還難用的平臺,最終被邊緣化或者被拋棄?
不會。因為效能和安全是不可兼得的,比如 File/FileReader/Blob 這種 API 天生就是為瀏覽器端沙盒環境設計的(WHATWG 好多標準都是為瀏覽器設計的)。Node.js 還沒有提供相應的 API,而且也不打算提供,畢竟在伺服器端環境我們更需要的是檔案系統,所以 Node.js 不去擁抱 WHATWG,而選擇了 POSIX。
既然談到了這個,那我就再講的深入一點吧,深入到底層看看為什麼 Node.js 不是用 Blob 和 FileReader 來讀取檔案。
在瀏覽器端檔案通常來自於網路,由 url 提供,或者來自於表單的使用者主動選擇。無論何種方式都是由瀏覽器來完成檔案的讀取,並把檔案內容載入到記憶體緩衝區中,這時 JavaScript 可以通過 Blob 來操作此檔案。但是 Node.js 卻沒有實現這些 API,而是在檔案系統之上構建了 Stream 模組來實現。再看看其伺服器端程式語言例如 Java、PHP 也都提供了 Stream。
如果我們把 Node.js 作為一個 Web 伺服器,那麼我們橫向和 Nginx 對比一下。如果使用 js 開發一個靜態檔案伺服器,那麼 Nginx 可以輕輕鬆鬆以十倍百倍的效能輾壓 Node.js。
我們可以從底層分析一下兩者為何相差懸殊。這裡有幾個知識點:
- 使用者空間
- 核心空間
- 程式上下文
- 中斷上下文
- DMA
- Zero Copy
為了安全考慮作業系統不允許使用者程式碼直接操作硬體,為了保證作業系統核心的安全,將空間劃分為兩部分,一部分為核心空間,一部分為使用者空間。使用者編寫的程式碼執行在使用者空間,當需要使用底層功能時,可以通過系統呼叫進入核心,例如檔案讀取。
當使用者程式通過系統呼叫從使用者空間進入到核心空間時,系統需要將使用者程式的上下文儲存起來,當再次從核心空間回到使用者空間時,系統恢復此上下文。
對於靜態伺服器,則這個步驟大概是:
- 呼叫 read,檔案被 copy 到核心緩衝區
- read 函式返回,檔案從核心緩衝區 copy 到使用者緩衝區
- write 函式呼叫,將檔案從使用者緩衝區 copy 到核心與 socket 相關的緩衝區
- 資料從 socket 緩衝區 copy 到相關協議引擎
可以看到檔案在整個過程中被 copy 了 4 次。
而 Nginx 底層使用 sendfile,可以實現 Zero Copy (零拷貝)。
整個流程變成了:
- sendfile 系統呼叫,檔案被 copy 至核心緩衝區
- 從核心緩衝區 copy 至核心中 socket 相關的緩衝區
- 從 socket 相關的緩衝區 copy 到協議引擎
可以看到在這個過程中,只有 3 次 copy。而且沒有了使用者空間和核心空間的切換,也不需要儲存和恢復程式的上下文。其實上面還有優化的餘地,因為在核心中發生了一次緩衝區到緩衝區的 copy。在 Linux 核心版本 2.4 之後,DMA 模組將資料直接從核心緩衝區傳遞給協議引擎。
雖然稱為 Zero Copy,但是資料依然是從磁碟複製到了記憶體,從作業系統的角度來看這個是必須的,所謂的零拷貝是指核心中沒有冗餘資料,資料不需要在核心拷貝。藉助 DMA 模組此過程完全不需要 CPU 參與。
在 Nginx 中只需要資料副本的 2 個 copy,而 Node.js 則需要 4 次。如果 Node.js 要想使用 Zero Copy 也有方法,比如使用 os 模組的功能,或者直接使用 C++ 擴充套件。
從另一個角度講,Node.js 的效能損耗不僅僅是 4 次 copy 以及程式上下文的儲存和恢復,還包括資料和程式碼從 C++ 到 JavaScript 的反覆跨越邊界。
我們回過頭來討論瀏覽器,對於瀏覽器來講,根本就不需要這個特性。因為瀏覽器中的 JavaScript 程式碼不僅僅是使用者空間執行,而且還是在沙盒空間執行。
所以與其在此猜測相容瀏覽器指的什麼,不如對比瀏覽器和伺服器的差別:
- 瀏覽器執行不受信程式碼,伺服器執行受信程式碼
- 瀏覽器遵循 W3/WHATWG,伺服器遵循 POSIX
- 瀏覽器關心 API 層的效能,伺服器更關心作業系統層的效能
- 瀏覽器能力受限,伺服器能力不受限
掃碼二維碼關注我的公眾號,每週推送原創前端內容