文章原創於公眾號:程式猿周先森。本平臺不定時更新,喜歡我的文章,歡迎關注我的微信公眾號。
想想好久沒有更新文章了,最近去了新公司,又拾起了被我拋下許久的後端了,不過因為公司的需求,後端採用node.js,最近一直在學習Node.js,隨著逐漸深入的瞭解,發現真的node能越來越變得熱門是有其存在的道理的。可能有人會說,java作為後端語言一直隱隱有龍頭老大的姿勢,為何我們還要去學node呢?Node.js究竟是什麼?它是新的語言還是新的框架,是新的工具抑或只是一個簡單的JavaScript檔案?
執行時環境我們眾所周知Java具有一個稱作JRE的執行時環境來使得java程式能夠順利執行。JRE有一個稱為JVM的虛擬機器。JVM有許多元件,如垃圾回收器(GC),即時(JIT)編譯器,直譯器,類裝載器,執行緒管理器,異常處理器,用於在不同時間執行不同的任務。JRE還有一系列的庫來幫助執行時的Java程式。
我們為什麼要突然牽扯到JRE執行時環境呢,其實正是為了與node作比較,Node不是一種語言,也不是框架,更不是工具,它是執行JavaScript應用程式的執行時環境。Node.js有一個稱為JavaScript Virtual Machine的虛擬機器。它為基於JavaScript的應用程式生成機器程式碼,以便在不同的平臺上啟用它。這個虛擬機器就是Google的V8引擎,也有主要元件,如JIT和GC,分別用於執行任務,執行時編譯,和記憶體管理。
發展潛力判斷Java和node的發展潛力可能要從其背後的生態社群和支援庫上切入,然而以Java為核心的傳統體系自然比不上node這樣的新勢力,簡而言之,Java成熟而龐大,node迅捷而活躍。java其功能性和實用性自然不必多說,但是java包含了大量的樣品程式碼,擾亂了程式猿所想表達的意圖,就不如java三大框架之一的spring,程式猿在使用spring的時候servlet,資料持久,以及構成系統的底層的東西,spring框架已經封裝好會幫助你處理這一切,我們只需要專注於寫業務層程式碼就足以。但是在Spring中,子系統一個接一個,哪怕你犯最微小的錯誤,它都會用讓你崩潰的異常來懲罰你。可能緊接著你就會看到巨大的異常資訊。裡面包含著一個一個你根本不知道的封裝好的方法,Spring做了許多工作來實現程式碼的功能。這種級別的抽象顯然需要大量的邏輯,長長的異常資訊不一定是壞事,它指出了一個症狀:這需要多少記憶體和效能上的額外開銷?spring是怎麼執行的?框架需要解析方法名、猜測程式設計師的意圖、構建類似於抽象語法樹的東西、生成SQL等等。這些事情的額外開銷有多大?所以說使用java掩蓋複雜性並不會因此簡單化,只會讓系統更復雜。java嚴格的型別檢查使得Java幫你避免許多型別的bug,因為不好的程式碼無法通過編譯。Java的強型別的缺點就是太多樣板程式碼。程式設計師要不斷進行型別轉換,程式設計師要花掉更多時間寫精確的程式碼,使用更多的樣板程式碼,以圖早期發現錯誤並改正。
而Node.js恰恰相反。執行緒會導致更復雜化的系統。所以Node.js採用輕量級,單執行緒的系統,利用了js的匿名函式進行非同步回撥,你只需要簡單的使用匿名函式,也就是閉包。不需要搜尋正確的抽象介面,只需要寫下業務程式碼,沒有任何冗餘。這就是使用Node.js的最大好處,不過非同步回撥自然也出現一個急需解決的問題:回撥陷阱。
在Node.js中,我們不斷巢狀回撥函式的同時,很容易就陷入回撥函式的陷阱中,每層巢狀都會讓程式碼更復雜,使得錯誤處理和結果處理更困難。一個相關的問題就是js語言不會幫助程式設計師恰當地表達非同步執行。其實有些庫會使用Promise來簡化非同步操作,但是看起來我們把問題簡單化了,但是事實上程式碼層面更復雜化了,Promise用了許多樣板程式碼,掩蓋了程式設計師的真實意圖。後來Node.js支援ES5與ES6,可以採用async/await函式重寫回撥函式。還是同樣的非同步結構,但使用了正常的迴圈結構來書寫。錯誤和結果處理的位置也很自然,程式碼更易於理解,更容易編寫,而且也可以很容易地理解程式設計師的意圖。回撥陷阱並不是用掩蓋複雜性的方式解決的。相反,語言和正規化的改變解決了回撥陷阱的問題,同時還解決了過多樣板程式碼的問題。有了async函式,程式碼就更漂亮了。簡單化的解決方法,將Node.js的缺點轉化為了優點。
但是JavaScript的型別很鬆散。而且在你書寫程式碼的時候不會進行報錯,許多型別不需要定義,通常也不需要用型別轉換。因此程式碼更清晰易讀,但存在漏掉編碼錯誤的風險,只有在編譯的時候才會去檢查你語法以及邏輯是否存在問題,所以在Node.js中,為了更好的除錯BUG,node支援將程式分成不同的模組,因為有模組的存在,將錯誤發生的範圍縮小到某個範圍內,使得Node.js模組更容易測試。
包管理java最重要的問題之一就是沒有統一的包管理系統,可能有人會和我說Maven。但是無論是用途、易用性還是功能上,Maven與Node.js的包管理系統相比簡直是天壤之別。npm 是 Node.js 官方提供的包管理工具,他已經成了 Node.js 包的標準釋出平臺,用於 Node.js 包的釋出、傳播、依賴控制。npm 提供了命令列工具,使你可以方便地下載、安裝、升級、刪除包,也可以讓你作為開發者釋出並維護包。最好的地方是npm程式碼庫不僅供Node.js使用,也可以讓前端工程師使用。所有的前端JavaScript庫都以npm包的形式存在。許多前端工具如Webpack都是用Node.js編寫的。
效能java使用HotSpot這個超級虛擬機器,它採用了多位元組編譯策略。它會檢測經常執行的程式碼,一段程式碼執行次數越多,就會應用越多的優化。因此HotSpot效能相對來說更快。Node底層選擇用c++和v8引擎來實現的,node.js的事件驅動機制,這意味著要面對大規模的http請求,node.js是憑藉事件驅動來完成的,效能部分是不用擔心的,並且很出色。而且,由於V8引擎的改進,Node.js的每次釋出都會帶來巨大的效能提升。
雖然Node對高併發應用有著極高的效能,但是Node.js也有著自己的缺點:
- Node不適合CPU密集型應用,因為CPU密集型應用如果有長時間的運算,不如大迴圈,將會導致CPU時間片不能釋放,使得後續的IO操作全部暫停。
- 而且Node只支援單核CPU,不能充分利用CPU資源
- 可靠性低,一旦程式碼某個環節崩潰,將會導致整個系統都崩潰,原因就在於Node是使用單程式。
- Node的開源元件庫質量參差不齊,更新快,而且不向下相容。
其實Node.js作為後端能實現幾乎所有應用,只是我們選擇的時候考慮更多的是專案場景是不是適合用NodeJS。
歡迎關注我個人公眾號:程式猿周先森