NodeJS簡介

起源

??Ryan Dahl是一名資深的C/C++程序員,在創(chuàng)造出Node之前,他的主要工作都是圍繞高性能 Web服務(wù)器進(jìn)行的。經(jīng)歷過一些嘗試和失敗之后,他找到了設(shè)計(jì)高性能,Web服務(wù)器的幾個要點(diǎn): 事件驅(qū)動、非阻塞I/O,而這也正是nodejs的兩大特點(diǎn)。
  所以Ryan Dahl最初的目標(biāo)是寫一個基于事件驅(qū)動、非阻塞I/O的Web服務(wù)器,以達(dá)到更高的性能,提供Apache等服務(wù)器之外的選擇。寫Node的時(shí)候,Ryan Dahl曾經(jīng)評估過C、Lua、Haskell、 Ruby等語言作為備選實(shí)現(xiàn),結(jié)論為:C的開發(fā)門檻高,可以預(yù)見不會有太多的開發(fā)者能將它用于日常的業(yè)務(wù)開發(fā),所以舍棄它;Ryan Dahl覺得自己還不足夠玩轉(zhuǎn)Haskell,所以舍棄它;Lua自身已經(jīng)含有很多阻塞I/O庫,為其構(gòu)建非阻塞I/O庫也不能改變?nèi)藗兝^續(xù)使用阻塞I/O庫的習(xí)慣,所以也舍棄它;而Ruby的虛擬機(jī)由于性能不好而落選。
  相比之下,JavaScript比C的開發(fā)門檻要低,比Lua的歷史包袱要少。盡管服務(wù)器端JavaScript存在已經(jīng)很多年了,但是后端部分一直沒有市場,可以說歷史包袱為零,為其導(dǎo)入非阻塞I/O庫沒有額外阻力。另外,JavaScript在瀏覽器中有廣泛的事件驅(qū)動方面的應(yīng)用,暗合Ryan Dahl喜好基于事件驅(qū)動的需求。當(dāng)時(shí),第二次瀏覽器大戰(zhàn)也漸漸分出高下,Chrome瀏覽器的JavaScript引擎V8摘得性能第一的桂冠。考慮到高性能、符合事件驅(qū)動、沒有歷史包袱這3個主要原因,JavaScript成為了Node的實(shí)現(xiàn)語言。
  起初,Ryan Dahl稱他的項(xiàng)目為web.js,就是一個Web服務(wù)器,但是項(xiàng)目的發(fā)展超過了他最初單純開發(fā)一個Web服務(wù)器的想法,變成了構(gòu)建網(wǎng)絡(luò)應(yīng)用的一個基礎(chǔ)框架,這樣可以在它的基礎(chǔ)上構(gòu)建更多的東西,諸如服務(wù)器、客戶端、命令行工具等。Node發(fā)展為一個強(qiáng)制不共享任何資源的單線程、單進(jìn)程系統(tǒng),包含十分適宜網(wǎng)絡(luò)的庫,為構(gòu)建大型分布式應(yīng)用程序提供基礎(chǔ)設(shè)施,其目標(biāo)也是成為一個構(gòu)建快速、可伸縮的網(wǎng)絡(luò)應(yīng)用平臺。它自身非常簡單,通過通信協(xié)議來組織許多Node,非常容易通過擴(kuò)展來達(dá)成構(gòu)建大型網(wǎng)絡(luò)應(yīng)用的目的。每一個Node進(jìn)程都構(gòu)成這個網(wǎng)絡(luò)應(yīng)用中的一個節(jié)點(diǎn),這是它名字所含意義的真諦。

特點(diǎn)

??作為后端JavaScript的運(yùn)行平臺,Node保留了前端瀏覽器JavaScript中那些熟悉的接口,沒有改寫語言本身的任何特性,依舊基于作用域和原型鏈,區(qū)別在于它將前端中廣泛運(yùn)用的思想遷移到了服務(wù)器端。Node相較于其他語言的特點(diǎn)如下所示:
  1、異步I/O
  在Node中,絕大多數(shù)的操作都以異步的方式進(jìn)行調(diào)用。Ryan Dahl排除萬難,在底層構(gòu)建了很多異步I/O的API,從文件讀取到網(wǎng)絡(luò)請求等,均是如此。這樣的意義在于,在Node中,我們可 以從語言層面很自然地進(jìn)行并行I/O操作。每個調(diào)用之間無須等待之前的I/O調(diào)用結(jié)束。在編程模型上可以極大提升效率。
  以同時(shí)執(zhí)行兩個文件讀取任務(wù)為例,異步I/O取決于最慢的那個文件讀取的耗時(shí),而同步I/O的耗時(shí)是兩個任務(wù)的耗時(shí)之和。這里異步帶來的優(yōu)勢是顯而易見的。
  2、事件
  隨著Web 2.0時(shí)代的到來,JavaScript在前端擔(dān)任了更多的職責(zé),事件也得到了廣泛的應(yīng)用。 Node不像Rhino那樣受Java的影響很大,而是將前端瀏覽器中應(yīng)用廣泛且成熟的事件引入后端, 配合異步I/O,將事件點(diǎn)暴露給業(yè)務(wù)邏輯。
  事件的編程方式具有輕量級、松耦合、只關(guān)注事務(wù)點(diǎn)等優(yōu)勢,但是在多個異步任務(wù)的場景下,事件與事件之間各自獨(dú)立,如何協(xié)作是一個問題。
  3、回調(diào)函數(shù)
  與其他的Web后端編程語言相比,Node除了異步和事件外,回調(diào)函數(shù)是一大特色??v觀下來,回調(diào)函數(shù)也是最好的接受異步調(diào)用返回?cái)?shù)據(jù)的方式。但是這種編程方式對于很多習(xí)慣同步思路編程的人來說,也許是十分不習(xí)慣的。代碼的編寫順序與執(zhí)行順序并無關(guān)系,這對他們可能造成閱讀上的障礙。在流程控制方面,因?yàn)榇┎辶水惒椒椒ê突卣{(diào)函數(shù),與常規(guī)的同步方式相比,變得不那么一目了然了。
  4、單線程
  JavaScript語言的一大特點(diǎn)就是單線程,也就是說,同一個時(shí)間只能做一件事。JavaScript的單線程,與它的用途有關(guān)。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。比如,假定JavaScript同時(shí)有兩個線程,一個線程在某個DOM節(jié)點(diǎn)上添加內(nèi)容,另一個線程刪除了這個節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個線程為準(zhǔn)?所以,為了避免復(fù)雜性,從一誕生,JavaScript就是單線程,這已經(jīng)成了這門語言的核心特征。
  Node保持了JavaScript在瀏覽器中單線程的特點(diǎn)。而且在Node中,JavaScript與其余線程是無法共享任何狀態(tài)的。單線程的最大好處是不用像多線程編程那樣處處在意狀態(tài)的同步問題,這里沒有死鎖的存在,也沒有線程上下文交換所帶來的性能上的開銷。
  同樣,單線程也有它自身的弱點(diǎn),具體有以下3方面:無法利用多核CPU;錯誤會引起整個應(yīng)用退出,應(yīng)用的健壯性值得考驗(yàn);大量計(jì)算占用CPU導(dǎo)致無法繼續(xù)調(diào)用異步I/O。
  像瀏覽器中JavaScript與UI共用一個線程一樣,JavaScript長時(shí)間執(zhí)行會導(dǎo)致UI的渲染和響應(yīng)被中斷。在Node中,長時(shí)間的CPU占用也會導(dǎo)致后續(xù)的異步I/O發(fā)不出調(diào)用,已完成的異步I/O的回調(diào)函數(shù)也會得不到及時(shí)執(zhí)行。
  HTML5定制了Web Workers的標(biāo)準(zhǔn),Web Workers能夠創(chuàng)建工作線程來進(jìn)行計(jì)算,以解決JavaScript大計(jì)算阻塞UI渲染的問題。工作線程為了不阻塞主線程,通過消息傳遞的方式來傳遞運(yùn)行結(jié)果,這也使得工作線程不能訪問到主線程中的UI。
  Node采用了與Web Workers相同的思路來解決單線程中大計(jì)算量的問題:child_process。 子進(jìn)程的出現(xiàn),意味著Node可以從容地應(yīng)對單線程在健壯性和無法利用多核CPU方面的問題。通過將計(jì)算分發(fā)到各個子進(jìn)程,可以將大量計(jì)算分解掉,然后再通過進(jìn)程之間的事件消息來傳遞結(jié)果,這可以很好地保持應(yīng)用模型的簡單和低依賴。通過Master-Worker的管理方式,也可以很好地管理各個工作進(jìn)程,以達(dá)到更高的健壯性。

應(yīng)用場景

??在進(jìn)行技術(shù)選型之前,需要了解一項(xiàng)新技術(shù)具體適合什么樣的場景,畢竟合適的技術(shù)用在合適的場景可以起到意想不到的效果。關(guān)于Node,探討得較多的主要有I/O密集型和CPU密集型。
  1、I/O密集型
  如果將所有的腳本語言拿到一處來評判,那么從單線程的角度來說,Node處理I/O的能力是值得豎起拇指稱贊的。通常, 說Node擅長I/O密集型的應(yīng)用場景基本上是沒人反對的。Node面向網(wǎng)絡(luò)且擅長并行I/O,能夠有效地組織起更多的硬件資源,從而提供更多好的服務(wù)。
  I/O密集的優(yōu)勢主要在于Node利用事件循環(huán)的處理能力,而不是啟動每一個線程為每一個請求服務(wù),資源占用極少

2、CPU密集型
  換一個角度,在CPU密集的應(yīng)用場景中,Node是否能勝任呢?實(shí)際上,V8的執(zhí)行效率是十分高的。單以執(zhí)行效率來做評判,V8的執(zhí)行效率是毋庸置疑的。
  CPU密集型應(yīng)用給Node帶來的挑戰(zhàn)主要是:由于JavaScript單線程的原因,如果有長時(shí)間運(yùn)行的計(jì)算(比如大循環(huán)),將會導(dǎo)致CPU時(shí)間片不能釋放,使得后續(xù)I/O無法發(fā)起。但是適當(dāng)調(diào)整和分解大型運(yùn)算任務(wù)為多個小任務(wù),使得運(yùn)算能夠適時(shí)釋放,不阻塞I/O調(diào)用的發(fā)起,這樣既可同時(shí)享受到并行異步I/O的好處,又能充分利用CPU。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容