讓I/O與CPU計算并行
Node 在*nix平臺洒忧,通過線程池實現(xiàn)(主線程和I/O線程)剖膳,在windows下使用IOCP(調(diào)用異步方法碘举,等待I/O完成后通知茎匠,執(zhí)行回調(diào)格仲,內(nèi)部也依靠線程池,但由系統(tǒng)內(nèi)核管理)诵冒,通過libuv層來兼容凯肋。
Node本身是多線程的,但其中的JavaScript是單線程汽馋、因為v8的限制侮东,但計算之類的都是在此線程,多線程只是I/O(磁盤豹芯,網(wǎng)絡(luò)等)悄雅,I/O有另外的線程池
事件循環(huán)
Node自身的執(zhí)行模型,在libuv中
在Node啟動時,創(chuàng)建一個類似while(true)的循環(huán)铁蹈,每循環(huán)一次成一個Tick,每次Tick查看是否有事件要處理宽闲,若有就處理事件和它的相關(guān)回調(diào)函數(shù)。在windows中基于IOCP,在*nix中基于多線程
在Tick過程中通過觀察者判斷是否有事件要處理
異步過程中最重要的就是請求對象握牧,所有狀態(tài)容诬、傳入?yún)?shù)、當(dāng)前方法和回調(diào)函數(shù)都封裝在此沿腰,javascript將對象組裝好览徒,送入I/O線程池后就結(jié)束了,I/O操作在線程池中等待請求對象被執(zhí)行
Tick在執(zhí)行過程中檢查線程池中是否有執(zhí)行完的請求矫俺,并加入I/O觀察者隊列中吱殉,然后再從觀察者取到可用的請求對象當(dāng)做事件處理掸冤,取出對象中的回調(diào)函數(shù)執(zhí)行,若有業(yè)務(wù)層callback再給js執(zhí)行
事件驅(qū)動的高性能服務(wù)器
基于事件驅(qū)動的非阻塞I/O模型
通過主循環(huán)加載事件觸發(fā)的方式來運行程序處理請求,無需為每一個請求創(chuàng)建額外的對應(yīng)線程
:操作系統(tǒng)因為線程少友雳,所以在上下文切換時代價很小稿湿,有助系統(tǒng)穩(wěn)定處理大量請求(但不適合密集運算),但用戶代碼不能并行執(zhí)行押赊,I/O可以
- 單線程保證運行安全,避免重入
另外一些異步api
定時器
- setTimeout()單次定時執(zhí)行 setInterval()多次定時執(zhí)行
原理和異步I/O類似流礁,將創(chuàng)建的定時器放到定時器觀察者內(nèi)部的紅黑樹,tick執(zhí)行時神帅,從紅黑樹中迭代取出定時器對象再姑,檢查是否超過定時時間,超過就形成事件并且立刻執(zhí)行回調(diào)函數(shù)找御。
問題
:定時不精確,如果某個循環(huán)占用時間過多霎桅,當(dāng)再輪到定時器執(zhí)行時就已經(jīng)超時了
process.nextTick()
- 若想立即異步執(zhí)行一個任務(wù),用這個更高效
setTimeout(function () { // TODO
}, 0);// 比較浪費性能
process.nextTick=function(callback){
if(process._exiting) return;
if(tickDepth >=process.maxTickDepth)
maxTickWarn();
var tock={callback:callback};
if(process.domain) tock.domain=process.domain;
nextTickQueue.push(tock);
if(nextTickQueue.length){
process._needTickCallback();
}
}
調(diào)用process.nextTick(),只會將回調(diào)函數(shù)放入隊列中滔驶,在下個Tick取出
setImmediate()
- 類process.nextTick() 將回調(diào)函數(shù)延時執(zhí)行遇革,建議使用這個(v0.9.1以后)
process.nextTick(function(){
console.log('nextTick延時執(zhí)行1')
});
process.nextTick(function(){
console.log('nextTick延時執(zhí)行2');
});
setImmdiate(function(){
console.log('setImmdiate延時執(zhí)行1');
process.nextTick(function(){
console.log('強勢插入');
})
});
setImmediate(function () {
console.log('setImmediate延時執(zhí)行2');
});
console.log('正常執(zhí)行');
//正常執(zhí)行
//nextTick 延時執(zhí)行1
//nextTick 延時執(zhí)行2
//setImmediate 延時執(zhí)行1
//強勢插入
//setImmediate 延時執(zhí)行2
process.nextTick()屬于idle觀察者,setImmediate()屬于check觀察者揭糕,在每輪循環(huán)中,idle觀察者先于I/O觀察者先與check
process.nextTick()的回調(diào)函數(shù)保存在數(shù)組中,在每輪循環(huán)中會將數(shù)組中的回調(diào)函數(shù)全部執(zhí)行完插佛。setImmediate()保存在鏈表中,在每輪循環(huán)中執(zhí)行鏈表的一個回調(diào)函數(shù)
異步并發(fā)控制
并發(fā)量過大,下層服務(wù)器會吃不消
bagpipe
- 通過隊列控制并發(fā)量
- 在當(dāng)前活躍的異步調(diào)用量小于限定值,從隊列中取出執(zhí)行
- 活躍調(diào)用達(dá)到限定值后氢拥,調(diào)用暫存在隊列中
- 每個調(diào)用結(jié)束時锨侯,從隊列中取出新的異步調(diào)用執(zhí)行