您的位置:網站首頁 > 電器維修資料網 > 正文 >
無阻塞編程 后端工程師的Node.js
來源: 日期:2013-11-20 18:39:48 人氣:標簽:
我想不僅僅是Node.js,當我們要引入任何一種新技術前都必須要搞清楚幾個問題:
1. 我們遇到了什么問題?
2. 這項新技術解決什么問題,是否契合我們遇到的問題?
3. 我們遇到問題的多種解決方案中,當前這項新技術的優勢體現在哪兒?
4. 使用新技術,帶來哪些新問題,嚴重么,我們能否解決掉?
我們的問題:Server端阻塞
Node.js被設計用來解決服務端阻塞問題.下面通過一段簡單的代碼解釋何為阻塞:
1
2
3
4
//根據ID,在數據庫中Persons表中查出Name
var name = db.query("select name from persons where id=1");
//進程等待數據查詢完畢,然后使用查詢結果。
output("name")
這段代碼的問題是在上面兩個語句之間,在整個數據查詢的過程中,當前程序進程往往只是在等待結果的返回.這就造成了進程的阻塞.對于高并發,I/O 密集行的網絡應用中,一方面進程很長時間處于等待狀態,另一方面為了應付新的請求不斷的增加新的進程.這樣的浪費會導致系統支持QPS遠遠小于后端數據服 務能夠支撐的QPS,成為了系統的瓶頸.而且這樣的系統也特別容易被慢鏈接攻擊(客戶端故意不接收或減緩接收數據,加長進程等待時間)。
如何解決阻塞問題
可以引入事件處理機制解決這個問題。在查詢請求發起之前注冊數據加載事件的響應函數,請求發出之后立即將進程交出,而當數據返回后再觸發這個事件并在預定好的事件響應函數中繼續處理數據:
1
2
3
4
5
6
//定義如何后續數據處理函數
function onDataLoad(name){
output("name");
}
//發起數據請求,同時指定數據返回后的回調函數
db.query("select name from persons where id=1",onDataLoad);
我們看到若按照這個思路解決阻塞問題,首先我們要提供一套高效的異步事件調度機制.而主要用于處理瀏覽器端的各種交互事件的JavaScript。相對于其他語言,至少有兩個關鍵點特別適合完成這個任務。
為什么JS適合解決阻塞問題
首先JavaScript是一種函數式編程語言,函數編程語言 重要的數學基礎是λ演算(lambda calculus) — 即函數對象可以作為其他函數對象的輸入(參數)和輸出(返回值)。
這個特性使得為事件指定回調函數變得很容易。特別是JavaScript還支持匿名函數。通過匿名函數的輔助,之前的代碼可以進行簡寫如下:
1
2
3
db.query("select name from persons where id=1",function(name){
output(name);
});
還有另一個關鍵問題是,異步回調的運行上下文保持(本文暫稱其為”狀態保持”)。我們先來看一段代碼來說明何為狀態保持:
1
2
3
4
5
6
7
//傳統同步寫法:將查詢和結果打印抽象為一個方法
function main(){
var id = "1";
var name = db.query("select name from persons where id=" + id);
output("person id:" + id + ", name:" + name);
}
main();
前面的寫法在傳統的阻塞是編程中非常常見,但接下來進行異步改寫時會遇到一些困擾:
1
2
3
4
5
6
7
8
//異步寫法:
function main(){
var id = "1";
db.query("select name from persons where id=" + id,function(name){
output("person id:" + id + ", name:" + name);//n秒后數據返回后執行回調
});
}
main();
細心的朋友可能已經注意到,當等待了n秒數據查詢結果返回后執行回調時。回調函數中卻仍然使用了main函數的局部變量”id”,而”id”似乎應 該在n秒前走出其作用域。為什么此時”id”仍然可以訪問呢,這是因為JavaScript的另外一個重要語言特性:閉包(Closures)。接下來我 來詳解閉包的原委。
在復雜的應用中,我們一定會遇到這類場景。即在函數運行時需要訪問函數定義時的上下文數據(注意:一定要區分函數定義時和函數運行時兩個不同的時 刻)。特別是在異步編程模型中,函數的定義和運行又分處不同的時間段,那么保持上下文的問題變得更加突出了。因為我們在任務執行一半時把資源交出去沒有問 題,但當任務需要再次繼續時我們必須還原現場。
在這個例子中,db.query作為一個公共的數據庫查詢方法,把”id”這個業務數據傳入給db.query,交由其保存是不太合適的。但我們可 以稍作抽象,讓db.query再支持一個需要保持狀態的數據對象傳入,當數據查詢完畢后可以把這些狀態數據原封不動的回傳。如下:
1
2
3
4
5
6
7
8
9
function main(){
var id = "1";
var currentState = new Object();
currentState.person_id = id;
db.query("select name from persons where id=" + id, function(name,state){
output("person id:" + state.person_id + ", name:" + name);
},currentState);//注意currentState是db.query的第三個參數
}
main();
記住這種重要的思路,我們再看看是否還能進一步的抽象?可以的,不過接下的動作之前,我們還要了解在JavaScript中一個函數也是一個對象。 一個函數實例fn除了函數體的定義之外,我們仍然可以在這個函數對象實例之本身擴展其他屬性,如fn.a=1;受到這個啟發我們嘗試把需要保持的狀態直接 綁定到函數實例上:
1
2
3
4
5
6
7
8
9
10
function main(){
var id = "1";
var currentState = new Object();
currentState.person_id = id;
function onDataLoad(name){
output("person id:" + onDataLoad.state.person_id + ", name:" + name);
}
onDataLoad.state = currentState ;//為函數指定state屬性,用于保持狀態
db.query("select name from persons where id=" + id, onDataLoad);
}
我們做了什么?生成了currentState對象,然后在函數onDataLoad定義時,將currentState綁定給 onDataLoad這個函數實例。那么在onDataLoad運行時,就可以拿到定義時的state對象了。JavaScript的閉包特性就是內置了 這個過程而已。
在每個JavaScript函數運行時,都有一個運行時內部對象稱為Execution Context,它包含如下Variable Object(VO,變量對象), Scope Chain(作用域鏈)和”this” Value三部分。如圖:
- 1
- 2
- 下一頁
【看看這篇文章在百度的收錄情況】
相關文章
- 上一篇: 提高編程技術的十個技巧
- 下一篇: 液晶彩電圖像縮放處理電路