深入淺析Node.js 事件循環(huán)
Node.js 是單進程單線程應用程序,但是通過事件和回調支持并發(fā),所以性能非常高。
(來源于Javascript是單線程又是異步的,但是這種語言有個共同的特點:它們是 event-driven 的。驅動它們的 event 來自一個異構的平臺。)
Node.js 的每一個 API 都是異步的,并作為一個獨立線程運行,使用異步函數調用,并處理并發(fā)。
Node.js 基本上所有的事件機制都是用設計模式中觀察者模式實現。
Node.js 單線程類似進入一個while(true)的事件循環(huán),直到沒有事件觀察者退出,每個異步事件都生成一個事件觀察者,如果有事件發(fā)生就調用該回調函數.
事件驅動模型
Node.js 使用事件驅動模型,當webserver接收到請求,就把它關閉然后進行處理,然后去服務下一個web請求。
當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。
這個模型非常高效可擴展性非常強,因為webserver一直接受請求而不等待任何讀寫操作。
(這也被稱之為非阻塞式IO或者事件驅動IO)
在事件驅動模型中,會生成一個主循環(huán)來監(jiān)聽事件,當檢測到事件時觸發(fā)回調函數。
整個事件驅動的流程就是這么實現的,非常簡潔。有點類似于觀察者模式,事件相當于一個主題(Subject),而所有注冊到這個事件上的處理函數相當于觀察者(Observer)。
Node.js 有多個內置的事件,我們可以通過引入 events 模塊,并通過實例化 EventEmitter 類來綁定和監(jiān)聽事件,如下實例:
//引入events模塊 var events = require('events'); //創(chuàng)建eventEmitter對象 var eventEmitter = new events.EventEmitter(); //創(chuàng)建事件處理程序 var connectHandler = function connected() { console.log('連接成功。'); //觸發(fā)data_received事件 eventEmitter.emit('data_received'); } //綁定connection事件處理程序 eventEmitter.on('connection', connectHandler); //使用匿名函數綁定data_received事件 eventEmitter.on('data_received', function(){ console.log('數據接收成功。'); }); //觸發(fā)connection事件 eventEmitter.emit('connection'); console.log("程序執(zhí)行完畢。");
以上代碼輸出的結果是:
連接成功。
數據接收成功。
程序執(zhí)行完畢。
上述的結果正如你想的一樣,通過這種事件驅動模型我們就可以實現異步操作的。
(例如,我們可以一邊讀取文件,一邊執(zhí)行其他命令,在文件讀取完成后,我們將文件內容作為回調函數的參數返回。這樣在執(zhí)行代碼時就沒有阻塞或等待文件 I/O 操作。這就大大提高了 Node.js 的性能,可以處理大量的并發(fā)請求。)
var fs = require("fs"); fs.readFile('input.txt', function (err, data) { if (err){ console.log(err.stack); return; } console.log(data.toString()); }); console.log("程序執(zhí)行完畢。");
以上代碼輸出的結果是:
程序執(zhí)行完畢。
input.txt的內容。
從上述代碼可以看出fs.readFile可以分發(fā)事件,當然Node.js里面的許多對象都會分發(fā)事件,一個net.Server對象會在每次有新連接時也會分發(fā)一個事件, 所有這些產生事件的對象都是 events.EventEmitter 的實例。
不過大多數時候我們不會直接使用 EventEmitter,而是在對象中繼承它。包括 fs、net、 http 在內的,只要是支持事件響應的核心模塊都是 EventEmitter 的子類。
為什么要這樣做呢?原因有兩點:
首先,具有某個實體功能的對象實現事件符合語義, 事件的監(jiān)聽和發(fā)射應該是一個對象的方法。
其次 JavaScript 的對象機制是基于原型的,支持 部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關系。
下面給大家分享我的第一個NodeJs項目
Node.js的安裝通常有兩種方式:自己編譯源代碼和使用編譯好的文件,我這里使用編譯好的文件
目前我的home目錄下有剛下載來的node-v4.2.3-linux-x64
1.首先解壓縮
tar xvf node-v4.2.3-linux-x64
2.設置鏈接,設置鏈接的目的在于任何路徑都能夠用到node命令
ln -s /home/node-v4.2.3-linux-x64/bin/node /usr/local/bin/node
ln -s /home/node-v4.2.3-linux-x64/bin/npm /usr/local/bin/npm
3.在home文件夾下建個目錄叫mynodeproj,然后再建了個文件叫server.js(當然也可以起別的名字)
var http = require('http'); http.createServer(function (request, response) { //發(fā)送 HTTP 頭部 //HTTP 狀態(tài)值: 200 : OK //內容類型: text/plain response.writeHead(200, {'Content-Type': 'text/plain'}); //發(fā)送響應數據 "Hello World" response.end('Hello World\n'); }).listen(8888); //終端打印如下信息 console.log('Server running at http://127.0.0.1:8888/');
4.使用node命令執(zhí)行以上代碼
node server.js
執(zhí)行上述命令之后會在命令行中顯示"Server runnint at
因為我這個服務器是在阿里云里跑起來的,所以只要在外面的瀏覽器中鍵入阿里云IP+端口就可以訪問了。