Node.js是一個JavaScript運行平臺
- Node.js是基于Google Chrome的JavaScript運行時建立的平臺照卦,用于搭建響應(yīng)速度快、易擴(kuò)展的網(wǎng)絡(luò)應(yīng)用。
- Node.js實際上是JavaScript運行環(huán)境,對Google V8引擎進(jìn)行了封裝智末。
- Node.js使用事件驅(qū)動機(jī)制、非阻塞I/O模型徒河,因此得以輕量和高效系馆。
- Node.js適用于分布式設(shè)備上運行數(shù)據(jù)密集型的實時應(yīng)用
Node.js所采用的GoogleV8引擎是基于ECMAScript2015開發(fā)的,ECMAScript2015是ECMAScript標(biāo)準(zhǔn)的第6個版本顽照,又稱為ES6由蘑,可簡寫為ES2015。
Node.js有什么優(yōu)點呢代兵?
Node.js和JavaScript的優(yōu)勢之一是它們的單線程編程模型尼酿,多個線程一般會引入bug,盡管一些新的編程語言奢人,包括Go谓媒、Rust試圖提供更加安全的并發(fā)工具,但Node.js仍然保留了JavaScript在瀏覽器中所用的模型何乎。在為瀏覽器編寫代碼時,編寫的指令序列依次執(zhí)行一條土辩,代碼并不是并行執(zhí)行的支救。然而對于用戶界面來說,這樣是不合理的拷淘。沒有用戶愿意在瀏覽器執(zhí)行網(wǎng)絡(luò)訪問或文件獲取這樣低速操作時等待各墨。為了解決這個問題,瀏覽器引入了事件機(jī)制:當(dāng)點擊按鈕時启涯,會有一個事件被觸發(fā)贬堵,有一個之前定義的函數(shù)會運行起來。事件機(jī)制可以規(guī)避在線程編程時經(jīng)常出現(xiàn)資源死鎖和靜態(tài)條件结洼。
- 單線程
Node.js單線程是指Node并沒有創(chuàng)建線程的能力黎做,所以代碼都是單線程執(zhí)行的。不過Node宿主環(huán)境并不是單線程的松忍,它維護(hù)一個執(zhí)行隊列蒸殿,循環(huán)檢測并調(diào)度JS線程來執(zhí)行,因此單線程執(zhí)行和并發(fā)操作并不沖突。 - 事件輪詢機(jī)制
Node.js 可在不斷新增額外線程的情況下宏所,依然對任務(wù)進(jìn)行并發(fā)處理酥艳。它是通過事件輪詢(event loop)來實現(xiàn)并行操作的。 - 非阻塞I/O
由于Node.js是事件驅(qū)動的爬骤,因此使用了事件循環(huán)來解決I/O操作帶來的瓶頸充石。在Node.js中一個I/O操作通常會帶有 一個回調(diào)函數(shù),當(dāng)I/O操作完畢并返回時霞玄,會調(diào)用此回調(diào)函數(shù)骤铃。與此同時,主線程則繼續(xù)執(zhí)行接下來的代碼溃列。 - V8虛擬機(jī)
Node.js是一個基于Google Chrome V8 Javascript引擎之上的平臺劲厌,可用于創(chuàng)建輕量級、快速听隐、可擴(kuò)展补鼻、事件驅(qū)動和非堵塞I/O的應(yīng)用。 - 事件驅(qū)動
Node.js使用事件驅(qū)動模型雅任,即當(dāng)Web Server接收到請求時风范,將其關(guān)閉然后進(jìn)行處理,然后去服務(wù)下一個Web請求沪么。當(dāng)請求完成后硼婿,被放回處理隊列中。當(dāng)?shù)竭_(dá)隊列開頭時禽车,結(jié)果被返回給客戶端寇漫。 - RESTful API
支持Web服務(wù)和動態(tài)Web應(yīng)用程序的多層架構(gòu),實現(xiàn)可重用性殉摔、可擴(kuò)展性州胳、組件可響應(yīng)性的清晰分離。開發(fā)人員可輕松使用AJAX和RESTful Web服務(wù)創(chuàng)建豐富網(wǎng)絡(luò)應(yīng)用逸月。
Node.js適用于那些場景呢栓撞?
- 面向服務(wù)的架構(gòu)
面向服務(wù)的架構(gòu)就是做號前后端的依賴分離,將所有業(yè)務(wù)的關(guān)鍵業(yè)務(wù)邏輯都封裝成RESTful接口調(diào)用碗硬,上層只需考慮如何用接口來構(gòu)建具體應(yīng)用瓤湘。這樣后臺程序員無需知道具體數(shù)據(jù)是如何從一個頁面?zhèn)鬟f到另一個頁面的,也無需知道用戶數(shù)據(jù)更新是通過AJAX異步獲取還是刷新頁面獲取的恩尾。
- RESTful API
RESTful API場景可處理數(shù)萬條連接請求弛说,該操作沒有太復(fù)雜的邏輯,僅僅就是請求API特笋,將數(shù)據(jù)進(jìn)行返回即可剃浇。簡而言之巾兆,其本質(zhì)是從數(shù)據(jù)庫中查找值并將其組成一個響應(yīng),由于這類響應(yīng)是很小的文本虎囚,同時連接請求也是很小的文本角塑,因此整體流量不高。
- AJAX請求應(yīng)用
大數(shù)據(jù)時代對個人用戶也面的定制信息已成為主流淘讥,當(dāng)緩存失效后需發(fā)起AJAX請求圃伶,此時應(yīng)用Node.js可響應(yīng)大量的并發(fā)請求。Node.js適用于高并發(fā)蒲列、I/O密集窒朋、少量業(yè)務(wù)邏輯情況下的AJAX請求。
Node.js 不適用那些場景呢蝗岖?
- 實時性要求很高的場景
例如工程交換機(jī)侥猩、工控機(jī)器人、DCS集控系統(tǒng)等抵赢。此類場景基本通過垃圾回收機(jī)制來管理系統(tǒng)內(nèi)存欺劳,因此Node.js將會影響響應(yīng)速度,并且難以優(yōu)化铅鲤。
- 計算密集惡性系統(tǒng)
計算密集型系統(tǒng)基本是C語言的天下划提,基于JavaScript語言的Node.js在計算性能上很難與C相比。
- 單一進(jìn)程控制大內(nèi)存的場景
由于Google V8引擎的設(shè)計原則邢享,在32bit下有1G最大內(nèi)存的限制鹏往,在64bit下有1.7GB的最大內(nèi)存限制。雖然Node.js的Buffer分配可以不超過此限制骇塘,但也會帶來垃圾回收機(jī)制上性能的退化伊履。
Node異步機(jī)制的種類
Node異步機(jī)制大致分為回調(diào)函數(shù)、pub/sub
模式(事件模式)款违、異步庫控制庫(async
湾碎、when
...)、promise
項目奠货、Generator項目等。
Node與V8
Node的動力源自Google V8 JavaScript引擎座掘,是由服務(wù)于Google Chrome的Chromium項目組開發(fā)的递惋,V8中一個值得稱道的特性是它會被JavaScript直接編譯為機(jī)器碼,另外還有一些代碼優(yōu)化特性溢陪,所以Node才能這么快萍虽。
Node的本地部件libuv負(fù)責(zé)處理I/O,V8負(fù)責(zé)JavaScript代碼的解釋和執(zhí)行形真。使用C++綁定層可將libev和V8結(jié)合杉编。
非阻塞I/O
在服務(wù)器編程中訪問磁盤和網(wǎng)絡(luò)這樣的I/O請求會比較慢超全,所以希望在讀取文件或通過網(wǎng)絡(luò)發(fā)送消息時,運行平臺不會阻塞業(yè)務(wù)邏輯的執(zhí)行邓馒。Node.js使用了三種技術(shù)來解決這個問題:事件嘶朱、異步API、非阻塞I/O光酣。
非阻塞I/O是一個底層術(shù)語疏遏,意思是說程序可以在做其它事情時發(fā)起一個請求來獲取網(wǎng)絡(luò)資源,當(dāng)網(wǎng)絡(luò)操作完成時救军,將會運行一個回調(diào)函數(shù)來處理這個操作結(jié)果财异。
例如:典型Node Web應(yīng)用程序
使用Web應(yīng)用庫Express來處理商店的訂單流程,為了購買商品唱遭,瀏覽器發(fā)起了一個請求戳寸,然后應(yīng)用程序檢查庫存,為用戶創(chuàng)建一個賬號并發(fā)送回執(zhí)郵件拷泽,同時會返回一個JSON格式的HTTP響應(yīng)給瀏覽器疫鹊。
這里同時在做的使用包括:
- 發(fā)送一件回執(zhí)郵件
- 更新數(shù)據(jù)庫來保存用戶詳細(xì)信息和訂單
數(shù)據(jù)庫是通過網(wǎng)絡(luò)訪問的,Node中的網(wǎng)絡(luò)訪問是非阻塞的跌穗,使用名為libuv的庫訪問操作系統(tǒng)的非阻塞網(wǎng)絡(luò)調(diào)用困鸥。libuv庫在Linux、MaxOS褐筛、Windows中的實現(xiàn)是不同的龄坪。
訪問硬盤時比如在生成回執(zhí)郵件并從硬盤中讀取郵件模板時,libuv庫會借助線程池模擬出一種使用非阻塞調(diào)用的假象羹唠。
在進(jìn)行速度較慢的處理時讓Node能做其它事情奕枢,是使用帶非阻塞I/O的異步API真正的好處。即便只有一個單線程佩微、單進(jìn)程的Node Web應(yīng)用缝彬,也可以同時處理上千個網(wǎng)絡(luò)訪客發(fā)起的連接。Node是如何做的哺眯,得先研究一下事件輪詢谷浅。
事件輪詢
例如:在典型的Node Web應(yīng)用程序中,響應(yīng)瀏覽器請求時奶卓,Node內(nèi)置的HTTP服務(wù)器庫即核心模塊http.Server
使用流一疯、事件、Node的HTTP請求解析器的組合來處理請求夺姑。在使用Express Web應(yīng)用庫添加的回調(diào)函數(shù)也是由它觸發(fā)的墩邀。這個回調(diào)函數(shù)會觸發(fā)數(shù)據(jù)庫查詢語言,最終應(yīng)用程序會使用HTTP發(fā)送JSON作為響應(yīng)盏浙。
整個過程使用了三種非阻塞網(wǎng)絡(luò)調(diào)用:
- 一個用于請求
- 一個用于數(shù)據(jù)庫
- 一個用于響應(yīng)
Node是如何調(diào)配這些網(wǎng)絡(luò)操作的呢眉睹?答案是事件輪詢event loop
荔茬。
事件輪詢是單項運行的先入先出隊列,需要經(jīng)歷幾個階段竹海,輪詢中每個迭代都需要運行的重要階段上圖已展示慕蔚。
- 首先計時器開始執(zhí)行,計時器都是用JavaScript的setTimeout和setInterval函數(shù)安排好的站削。
- 接著是運行I/O回調(diào)坊萝,即觸發(fā)提前編寫的回調(diào)函數(shù)。
- 輪詢階段會去獲取新的I/O事件
- 最后使用setImmediate安排回調(diào)
本例是個特例许起,因為允許將回調(diào)安排在當(dāng)前隊列中的I/O回調(diào)完成之后立即執(zhí)行