Nodejs簡(jiǎn)介
Node.js? is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
Node.js是服務(wù)端JavaScript環(huán)境,是服務(wù)器關(guān)鍵功能的基礎(chǔ),比如二進(jìn)制數(shù)據(jù)操作、文件I/O操作、數(shù)據(jù)庫(kù)訪問(wèn)、計(jì)算機(jī)網(wǎng)絡(luò)等等篓吁。它有一些獨(dú)一無(wú)二的特性,使得它在眾多現(xiàn)有的成熟框架中凸顯出來(lái)蚪拦,比如Django?(Python),?Laravel(PHP),?RoR?(Ruby)等等杖剪。一些技術(shù)公司?PayPal,?Tinder,?Medium,LinkedIn,?Netflix因?yàn)檫@些特性而使用它,有些甚至在1.0版本就已經(jīng)開(kāi)始使用外盯。
Node.js Architecture
Bindings:這時(shí)候你可能會(huì)發(fā)現(xiàn)Node.js是用JavaScript和C/C++寫(xiě)的箱熬。bindings把Node.js內(nèi)部的C/C++類庫(kù)(c-ares, zlib, OpenSSL, http-parser等)暴露給JavaScript类垦,寫(xiě)bindings的一個(gè)動(dòng)機(jī)是代碼復(fù)用,另一個(gè)動(dòng)機(jī)是性能(CPU密集操作)
C/C++ Addons:Bindings只對(duì)Node.js的內(nèi)部核心類庫(kù)綁定城须,比如zlib, OpenSSL, c-ares, http-parser等等蚤认。如果想引入第三方的C/C++類庫(kù)到你的應(yīng)用,就必須自己寫(xiě)綁定代碼糕伐。自己寫(xiě)的綁定代碼叫做addons砰琢。可以把bindings良瞧、addons當(dāng)做JavaScript和C/C++之間的“橋梁”陪汽。
V8:高性能JavaScript執(zhí)行引擎,是谷歌開(kāi)源軟件褥蚯,C++語(yǔ)言實(shí)現(xiàn)挚冤。它也是Chrome瀏覽器的內(nèi)部引擎。JavaScript腳本被V8編譯成機(jī)器碼(因此非吃奘快)训挡,然后執(zhí)行澳骤。
libuv: 異步特性的C語(yǔ)言庫(kù)。它包含時(shí)間輪詢澜薄,線程池为肮,文件系統(tǒng)事件和一些提供關(guān)鍵功能的子進(jìn)程。
其他C/C++組件:比如c-ares肤京,crypto(OpenSSL)弥锄,http-parser,zlib蟆沫。這些底層組件的交互為服務(wù)器提供網(wǎng)絡(luò)、壓縮和編解碼等重要功能
Nodejs工作流
一個(gè)Node.js應(yīng)用啟動(dòng)温治,V8引擎開(kāi)始執(zhí)行你寫(xiě)的代碼饭庞。應(yīng)用中的對(duì)象(注冊(cè)事件的函數(shù))會(huì)變成一系列的觀察者。事件發(fā)生的時(shí)候熬荆,相應(yīng)的觀察者會(huì)得到通知舟山。
事件發(fā)生,觀察者的回調(diào)函數(shù)會(huì)被加入消息隊(duì)列?卤恳。只要消息隊(duì)列有數(shù)據(jù)累盗,循環(huán)函數(shù)?會(huì)不停取出它們壓入執(zhí)行堆棧?。注意突琳,只有先前的消息處理完成循環(huán)函數(shù)?才會(huì)把下一個(gè)壓入執(zhí)行堆棧?若债。
執(zhí)行堆棧中,如果發(fā)生I/O操作拆融,會(huì)把它移交到libuv處理蠢琳。libuv默認(rèn)包含一個(gè)有四個(gè)工作線程的線程池,線程的數(shù)量可以設(shè)置镜豹。工作線程通過(guò)和Node.js的底層類庫(kù)交互來(lái)執(zhí)行比如數(shù)據(jù)傳輸傲须、文件訪問(wèn)等操作。libuv處理完后再把事件加入消息隊(duì)列趟脂,Node.js主線程繼續(xù)處理泰讽。libuv以異步方式處理,Node.js主線程不會(huì)等待處理結(jié)果而是繼續(xù)執(zhí)行昔期。libuv處理完成已卸,加入消息隊(duì)列,循環(huán)函數(shù)再次把事件壓入執(zhí)行堆棧镇眷,這就是Node.js一個(gè)消息處理的生命周期咬最。
CPU密集型任務(wù)
因?yàn)閑vent loop在處理所有的任務(wù)/事件時(shí),都是沿著事件隊(duì)列順序執(zhí)行的欠动,所以在其中任何一個(gè)任務(wù)/事件本身沒(méi)有完成之前永乌,其它的回調(diào)惑申、監(jiān)聽(tīng)器、超時(shí)翅雏、nextTick()的函數(shù)都得不到運(yùn)行的機(jī)會(huì)圈驼,因?yàn)楸蛔枞膃vent loop根本沒(méi)機(jī)會(huì)處理它們,此時(shí)程序最好的情況是變慢望几,最糟的情況是停滯不動(dòng)绩脆,像死掉一樣。所以當(dāng)Node.js遇到高CPU占用率的任務(wù)時(shí)橄抹,event loop會(huì)被阻塞住靴迫,形成下面這種局面:
CPU密集型任務(wù)處理方法
被閑置的CPU內(nèi)核
Node.js是單線程程序,它只有一個(gè)event loop楼誓,也只占用一個(gè)CPU/內(nèi)核∮裥浚現(xiàn)在大部分服務(wù)器都是多CPU或多核的,當(dāng)Node.js程序的event loop被CPU密集型的任務(wù)占用疟羹,導(dǎo)致有其它任務(wù)被阻塞時(shí)主守,卻還有CPU/內(nèi)核處于閑置的狀態(tài),造成資源的浪費(fèi)榄融。
把CPU密集型任務(wù)分給子線程
child_process.fork() 得到的并不是子進(jìn)程参淫,而是一個(gè)全新的Node.js程序?qū)嵗麻_(kāi)進(jìn)程愧杯,通過(guò)IPC通信涎才,將CPU密集型任務(wù)交給子進(jìn)程簿晓,子進(jìn)程計(jì)算完畢后苞轿,再通過(guò)ipc消息通知主進(jìn)程,并將結(jié)果返回給主進(jìn)程
并且每個(gè)新實(shí)例至少需要30ms的啟動(dòng)時(shí)間和10M內(nèi)存牧嫉,也就是說(shuō)通過(guò)fork()繁衍進(jìn)程畏邢,不光是充分利用了CPU业扒,也需要很多內(nèi)存,所以不能fork()太多舒萎。進(jìn)程間通信效率也不高
Cluster
A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load.
addon
不開(kāi)進(jìn)程程储,而是將CPU耗時(shí)操作交給進(jìn)程內(nèi)的一個(gè)工作線程完成。
基本模塊
因?yàn)镹ode.js是運(yùn)行在服務(wù)區(qū)端的JavaScript環(huán)境臂寝,服務(wù)器程序和瀏覽器程序相比章鲤,最大的特點(diǎn)是沒(méi)有瀏覽器的安全限制了,而且咆贬,服務(wù)器程序必須能接收網(wǎng)絡(luò)請(qǐng)求败徊,讀寫(xiě)文件,處理二進(jìn)制內(nèi)容掏缎,所以皱蹦,Node.js內(nèi)置的常用模塊就是為了實(shí)現(xiàn)基本的服務(wù)器功能煤杀。這些模塊在瀏覽器環(huán)境中是無(wú)法被執(zhí)行的,因?yàn)樗鼈兊牡讓哟a是用C/C++在Node.js運(yùn)行環(huán)境中實(shí)現(xiàn)的沪哺。
global
JavaScript有且僅有一個(gè)全局對(duì)象沈自,在瀏覽器中,叫window對(duì)象辜妓。而在Node.js環(huán)境中枯途,也有唯一的全局對(duì)象,但不叫window籍滴,而叫g(shù)lobal酪夷,這個(gè)對(duì)象的屬性和方法也和瀏覽器環(huán)境的window不同。
process
process也是Node.js提供的一個(gè)對(duì)象孽惰,它代表當(dāng)前Node.js進(jìn)程捶索。JavaScript程序是由事件驅(qū)動(dòng)執(zhí)行的單線程模型,Node.js也不例外灰瞻。Node.js不斷執(zhí)行響應(yīng)事件的JavaScript函數(shù),直到?jīng)]有任何響應(yīng)事件的函數(shù)可以執(zhí)行時(shí)辅甥,Node.js就退出了酝润。
判斷JavaScript執(zhí)行環(huán)境
有很多JavaScript代碼既能在瀏覽器中執(zhí)行,也能在Node環(huán)境執(zhí)行璃弄,但有些時(shí)候要销,程序本身需要判斷自己到底是在什么環(huán)境下執(zhí)行的,常用的方式就是根據(jù)瀏覽器和Node環(huán)境提供的全局變量名稱來(lái)判斷:
fs
stream
http
crypto
Nodejs開(kāi)發(fā)桌面應(yīng)用
通過(guò)JavaScript夏块、HTML疏咐、CSS開(kāi)發(fā)跨平臺(tái)的桌面化應(yīng)用
Node-webkit
Electron
參考:
https://medium.com/yet-another-node-js-blog/architecture-of-node-js-internal-codebase-57cd
http://www.liaoxuefeng.com/