1炊林、node.js 和 JavaScript
不知道有沒有人和我一樣分不清楚node.js和JavaScript的關(guān)系的人,尤其是對(duì)node.js有一點(diǎn)點(diǎn)了解的卷要,比如當(dāng)我知道console.log()是node.js的Console類的時(shí)候我就更分不清它倆的關(guān)系了渣聚。我來理一理他們的關(guān)系:
JavaScript是一門語言,node.js不是一門語言僧叉,它僅僅就是用于運(yùn)行普通JavaScript代碼的東西奕枝。node.js誕生之前,JavaScript只能運(yùn)行于瀏覽器瓶堕,現(xiàn)在也可以在服務(wù)器端運(yùn)行于node.js隘道。
2、node.js和v8引擎
所有瀏覽器都有運(yùn)行網(wǎng)頁上JavaScript的JavaScript引擎。Firefox有叫做Spidermonkey的引擎谭梗,Safari有JavaScriptCore忘晤,Chrome有V8。node.js的創(chuàng)造者受到Chrome和V8的啟發(fā)默辨,Node.js很快就面世了德频。node.js就是帶有能操作I/O和網(wǎng)絡(luò)庫的V8引擎,因此你能夠在瀏覽器之外使用JavaScript創(chuàng)建shell腳本和后臺(tái)服務(wù)或者運(yùn)行在硬件上
關(guān)于v8:它使用C++寫成缩幸,它革命性的創(chuàng)舉是將Javascript源碼預(yù)編譯為機(jī)器碼壹置,而不是像以前那樣將Javascript翻譯為字節(jié)碼,然后在運(yùn)行時(shí)使用JIT動(dòng)態(tài)執(zhí)行代碼
node.js和V8的關(guān)系:node.js=V8+內(nèi)置基本模塊(大多用JavaScript編寫)表谊,類似JRE=JVM+java標(biāo)準(zhǔn)庫钞护。
3、node.js內(nèi)部結(jié)構(gòu)
從Node.js 的內(nèi)部結(jié)構(gòu)圖爆办,我們可以看到难咕,自底向上主要可以分成三層:最底層是 Node.js 依賴的各種庫,有 V8距辆、libuv 等余佃;中間層是各種 Binding,也就是膠水代碼跨算;最上層是應(yīng)用代碼爆土,可使用 Node.js 的各種 API。
底層依賴:
(1)V8:Google 開源的高性能 JavaScript 引擎诸蚕,它將 JavaScript 代碼轉(zhuǎn)換成機(jī)器碼步势,然后執(zhí)行,因此速度非潮撤福快坏瘩。V8 以 C++ 語言開發(fā)。
(2)libuv:libuv 以 C 語言開發(fā)漠魏,內(nèi)部管理著一個(gè)線程池倔矾。在此基礎(chǔ)之上,提供事件循環(huán)(Event Loop)柱锹、異步網(wǎng)絡(luò) I/O哪自、文件系統(tǒng) I/O等能力。
(3)其他底層依賴:如?c-ares奕纫、crypto (OpenSSL)提陶、http-parser?以及?zlib。這些依賴提供了對(duì)系統(tǒng)底層功能的訪問匹层,包括網(wǎng)絡(luò)隙笆、壓縮锌蓄、加密等。
中間層:
(1)Binding:Node.js 底層的依賴庫撑柔,有的以 C 語言開發(fā)瘸爽,有的以 C++ 語言開發(fā),如何讓應(yīng)用代碼(JavaScript)能夠與這些底層庫相互調(diào)用呢铅忿?這就需要中間層的 Binding 來完成剪决。Binding 是一些膠水代碼,能夠把不同語言綁定在一起使其能夠互相溝通檀训。在 Node.js 中柑潦,binding 所做的就是把 Node.js 那些用 C/C++ 寫的庫接口暴露給 JS 環(huán)境。
(2)Addon:中間層中峻凫,除了 Binding渗鬼,還有 Addon。Binding 僅橋接 Node.js 核心庫的一些依賴荧琼,如果你想在應(yīng)用程序中包含其他第三方或者你自己的 C/C++ 庫的話譬胎,需要自己完成這部分膠水代碼。你寫的這部分膠水代碼就稱為 Addon命锄。本質(zhì)上都是完成橋接的作用堰乔,使得應(yīng)用與底層庫能夠互通有無。
應(yīng)用層:
應(yīng)用層的代碼脐恩,我們開發(fā)的應(yīng)用镐侯、npm 安裝的包都運(yùn)行在這里。
4被盈、node.js的特點(diǎn)
(1)單線程:不同于Java析孽、PHP或者.net等服務(wù)器端語言中搭伤,會(huì)為每一個(gè)客戶端連接創(chuàng)建一個(gè)新的線程只怎。而node.js不為每個(gè)客戶連接創(chuàng)建一個(gè)新的線程,而僅僅使用一個(gè)線程怜俐。當(dāng)有用戶連接了墨榄,就觸發(fā)一個(gè)內(nèi)部事件周霉,通過非阻塞I/O、事件驅(qū)動(dòng)機(jī)制,讓Node.js程序宏觀上也是并行的坏逢。
(2)非阻塞I/O:
Apache請(qǐng)求數(shù)據(jù)庫的代碼:
在傳統(tǒng)的單線程處理機(jī)制中,在執(zhí)行了訪問數(shù)據(jù)庫代碼之后馋嗜,整個(gè)線程都將暫停下來辐啄,等待數(shù)據(jù)庫返回結(jié)果,才能執(zhí)行后面的代碼景鼠。I/O阻塞了代碼的執(zhí)行仲翎,極大地降低了程序的執(zhí)行效率。
node.js請(qǐng)求數(shù)據(jù)庫代碼:
非阻塞型I/O機(jī)制,因此在執(zhí)行了訪問數(shù)據(jù)庫的代碼之后溯香,將立即轉(zhuǎn)而執(zhí)行其后面的代碼鲫构,把數(shù)據(jù)庫返回結(jié)果的處理代碼放在回調(diào)函數(shù)中,從而提高了程序的執(zhí)行效率玫坛。
(3)事件驅(qū)動(dòng) event-driven:
在Node中结笨,客戶端請(qǐng)求建立連接,提交數(shù)據(jù)等行為湿镀,會(huì)觸發(fā)相應(yīng)的事件炕吸。Node在一個(gè)時(shí)刻,只能執(zhí)行一個(gè)事件回調(diào)函數(shù)勉痴,但是在執(zhí)行一個(gè)事件回調(diào)函數(shù)的中途算途,可以轉(zhuǎn)而處理其他事件(比如,又有新用戶連接了)蚀腿,然后返回繼續(xù)執(zhí)行原事件的回調(diào)函數(shù)嘴瓤,這種處理機(jī)制,稱為“事件環(huán)”機(jī)制莉钙。
一個(gè) Node.js 應(yīng)用啟動(dòng)——>V8 引擎會(huì)執(zhí)行應(yīng)用代碼——>Binding 把 Node.js 中用 C/C++ 寫的庫接口暴露給 JS 環(huán)境——>(事件發(fā)生時(shí))回調(diào)函數(shù)會(huì)被加進(jìn)一個(gè)事件隊(duì)列——>事件循環(huán)會(huì)持續(xù)把回調(diào)函數(shù)從隊(duì)列中拿出并執(zhí)行——>文件系統(tǒng)的I/O 請(qǐng)求和 DNS 相關(guān)請(qǐng)求都會(huì)放進(jìn)libuv線程池處理(其他的請(qǐng)求廓脆,如網(wǎng)絡(luò)、平臺(tái)特性相關(guān)的請(qǐng)求會(huì)分發(fā)給相應(yīng)的系統(tǒng)處理單元進(jìn)行處理)——>完成之后觸發(fā)相應(yīng)事件磁玉,對(duì)應(yīng)的事件回調(diào)函數(shù)會(huì)被放入事件隊(duì)列停忿。
總結(jié):事件循環(huán)機(jī)制保證了node.js的單線程、非阻塞I/O兩大特性得以實(shí)現(xiàn)蚊伞。
參考文檔: