node的特色javascript
事件驅動,非阻塞I/O前端
做爲後臺的javascript,node沒有改寫語言自己的任何特性,依舊基於做用域和原型鏈java
1 異步I/Onode
提起異步,用ajax來形容就會比較容易理解ajax
$.post('/url',{ key : 'value'}, function(data){編程
console.log('收到響應');後端
});瀏覽器
console.log('發送ajax結束')服務器
發起ajax請求後 "收到響應" 是在 "發送ajax結束"以後輸出的,網絡
在調用$.post()後,後續代碼是立刻執行的,而回調函數的執行時間是不被預期的,
咱們只知道它將在這個異步請求結束後執行,但並不知道具體的時間點,
異步調用中對於結果值的捕獲是符合"Don't call me, I will call you"的原則的
這也是注重結果 不關心過程的一種表現
在node中,異步I/O也很常見
var fs = require('fs');
fs.readFile('/path', function(){
console.log('讀取文件完成')
});
console.log('發起讀取文件');
這個例子與上一個ajax例子相同
"發起讀取文件" 會在 "讀取文件完成"以前輸出
一樣"讀取文件完成" 執行也取決文件的異步調用什麼時候結束
在node中,絕大多數操做都以異步的方式進行調用
咱們能夠從語言層面很天然地進行並行I/O操做
2 事件驅動 回調函數
node將前端瀏覽器中應用普遍且成熟的事件引入後端
配合異步I/O,將時間點暴露給業務邏輯
例子
var http = require('http');
var querystring = require('querystring');
//偵聽服務器的request事件
http.createServer(function(req, res){
var postData = '';
req.setEncoding('utf8');
//偵聽請求的data事件
req.on('data', function(trunk){
postData += trunk;
});
//偵聽請求的end事件
req.on('end', function(){
res.end(postData);
});
}).listen(8080);
console.log('服務器啓動完成')
不管在前端仍是後端,事件都是經常使用的
事件的編程方式具備輕量級 鬆耦合 只關注事務點等優點
可是在多個異步任務的場景下,事件與事件之間各自獨立 如何協助是一個問題
而node與其餘的Web後端語言相比,node除了異步和事件外,回調函數是一大特點
縱觀下來,回調函數也是最好的接受異步調用返回數據的方式.
3 單線程
node保持了javascript在瀏覽器中單線程的特點
並且在node中,javascript與其他線程是沒法共享任何狀態的
單線程的最大好處是不用像多線程編程那樣到處在乎狀態的同步問題,
這裏沒有死鎖的存在,也沒有線程上下文交換所帶來的性能上的開銷
單線程缺點
沒法利用多核CPU
錯誤會引發整個應用退出 應用的健壯性值得考驗
大量計算佔用CPU致使沒法繼續調用異步I/O
就像瀏覽器中javascript與UI共用一個線程同樣,
javascript長時間執行會致使UI的渲染和響應被中斷,
而在node中,長時間的CPU佔用也會致使後續的異步I/O發不出調用,
已完成的異步I/O的回調函數也會得不到及時執行
HTML5定製了Web Workers的標準後解決了前端javascript大計算阻塞UI渲染的問題
Web Workers可以建立工做線程來進行計算,
工做線程爲了避免阻塞主線程,經過消息傳遞的方式來傳遞運行結果
這也使得工做線程不能訪問到主線程中的UI
node採用了與Web Workers相同的思路來解決單線程中大計算量的問題
child_process
子進程的出現,意味着node能夠從容地應對單線程在健壯性和沒法利用多核CPU方面的問題
經過將計算分發到各個子進程,能夠將大量計算分解到,
而後再經過進程之間的事件消息來傳遞結果,
這能夠很好地保持應用模型的簡單和低依賴
經過Master-Worker的管理方式 也能夠很好地管理各個工做進程 以達到更高的健壯性
node應用場景
I/O密集型
一般,說node擅長I/O密集型的應用場景基本上是沒人反對的
node面向網絡且擅長並行I/O,可以有效地組織起更多的硬件資源 從而提供更多好的服務
優點主要在於node利用事件循環的處理能力,而不是啓動每個線程爲每個請求服務,資源佔用極少
CPU密集型
CPU密集型應用給node帶來的挑戰主要是:因爲javascript單線程的緣由
若是有長時間運行的計算(如大循環),將會致使CPU時間片不能釋放,使得後續I/O沒法發起
可是適當調整和分解大型運算任務爲多個小任務,使得運算可以適時釋放,不阻塞I/O調用的發起
這樣既可同時享受到並行異步I/O的好處,又能充分利用CPU
關於CPU密集型應用,node的異步I/O已經解決了在單線程上CPU與I/O之間阻塞沒法重疊利用的問題
I/O阻塞形成的性能浪費遠比CPU的影響小
node雖然沒有提供多線程用於計算支持,但還有如下兩個方式來充分利用CPU
1)node能夠經過編寫C/C++擴展的方式更高效地利用CPU,
將一些V8不能作到的性能極致的地方經過C/C++來實現
2)若是單線程的node不能知足知足需求,甚至用了C/C++擴展後還以爲不足
那麼經過子進程的方式,將一部分node進程當作常駐服務進程用於計算,
而後經過進程間的消息來傳遞結果,將計算與I/O分離,這樣還能充分利用多CPU.