node.js的事件機(jī)制
首先, 補(bǔ)充下對(duì)node 的理解:
nodeJs 是一個(gè)單進(jìn)程單線程應(yīng)用程序, 但是通過事件和回調(diào)支持并發(fā), 所以性能非常高~
那么什么是單進(jìn)程單線程呢~(寫給語文跟我一樣不好的小伙伴)
我們來看下單進(jìn)程和多進(jìn)程的區(qū)別:
1. 多進(jìn)程的優(yōu)勢(shì)在于任務(wù)的獨(dú)立性,比如某個(gè)任務(wù)單獨(dú)作為一個(gè)進(jìn)程的話,崩潰只影響自己的服務(wù),其他任務(wù)不受影響.如果是多個(gè)任務(wù)在同一個(gè)進(jìn)程內(nèi)部利用多個(gè)線程進(jìn)行處理,某個(gè)線程發(fā)生了未處理的異常的話,會(huì)導(dǎo)致整個(gè)進(jìn)程完蛋,所有的任務(wù)跟著遭殃
2. 從資源分配上來說,多進(jìn)程方案比多線程方案更加靈活和自由
3. 不過任務(wù)間的通信方面多進(jìn)程要比多線程復(fù)雜些,編一個(gè)好的多進(jìn)程通信方案要比多線程間的通信方案困難多了(小伙伴們注意區(qū)分進(jìn)程和線程喲~)
以web server為例的話,比如我的服務(wù)器上架設(shè)了三個(gè)網(wǎng)站,如果是用一個(gè)進(jìn)程管理的話, 網(wǎng)站A遭受攻擊死掉了,意味著另外兩個(gè)網(wǎng)站會(huì)出現(xiàn)同樣的現(xiàn)象. 如果是分開獨(dú)立的進(jìn)程的話,三個(gè)網(wǎng)站互不影響
具體來分呢, 單進(jìn)程對(duì)比多進(jìn)程有什么優(yōu)點(diǎn)呢:
1) 初期實(shí)現(xiàn)起來比較簡(jiǎn)單快速, 而且不用考慮進(jìn)程間通信的工作量
2) 單一性使得部署和運(yùn)營(yíng)比較簡(jiǎn)單(這還用說 / 白眼ing)
3) 內(nèi)存占用少, 不過呢, 現(xiàn)在內(nèi)存很廉價(jià), 但是一分錢也是錢呀!
4) 進(jìn)程內(nèi)部通信效率比IPC/scoket(多進(jìn)程數(shù)據(jù)通訊的終端)等要高效, 我一嗓子你就聽見了, 就不用費(fèi)力氣裝個(gè)電話了
當(dāng)然, 肯定優(yōu)缺點(diǎn)!不然花那么多錢開多進(jìn)程的人也太蠢了 ~
單進(jìn)程對(duì)比多進(jìn)程的缺點(diǎn)~
1) 中后期隨著業(yè)務(wù)邏輯的復(fù)雜化和需求的增加,這個(gè)單進(jìn)程會(huì)變得臃腫, 難以維護(hù)。 一個(gè)任務(wù)分解成多個(gè)進(jìn)程會(huì)使單個(gè)進(jìn)程的邏輯簡(jiǎn)單,而不容易出錯(cuò)
2) 同進(jìn)程內(nèi)模塊間是強(qiáng)依賴關(guān)系,需要在一起編譯相互的影響也比較大。 這相對(duì)于多進(jìn)程間通信來說, 耦合度較大(不符合高內(nèi)聚低耦合的偉大思想), 不利于多團(tuán)隊(duì)并行開發(fā)。 多進(jìn)程更便于多語言的協(xié)作開發(fā)。
3)任何模塊的崩潰都將導(dǎo)致整個(gè)進(jìn)程的失效,多進(jìn)程模式更加穩(wěn)定健壯,業(yè)務(wù)處理程序隔離運(yùn)行, 一個(gè)go home不會(huì)影響其他(你敢崩我也崩);
4) 性能問題: 如果不支持進(jìn)程間數(shù)據(jù)通訊的話,單進(jìn)程的容量是受限的, 這個(gè)性能瓶頸對(duì)于支持群組類服務(wù)的尤其需要考慮。多進(jìn)程部署極其靈活,可以擴(kuò)充機(jī)器數(shù)量來提高系統(tǒng)處理性能,還可以從硬件上避免單點(diǎn)故障。(一個(gè)人承受不來)
5) 單進(jìn)程中多線程難調(diào)試( 一槍開出去, 一群人倒了, 我還得檢查一下誰中槍了才能給你debug)
舉個(gè)小栗子
你有一個(gè)對(duì)象, 對(duì)象特別挑食, 但是對(duì)象只喜歡一種菜, 你每天做給她吃。
這就是個(gè)單進(jìn)程單線程的模型, 如果你做的不好吃了, 對(duì)象不吃了。
但是我有一個(gè)對(duì)象, 她喜歡吃10種菜, 我每天端過去10份, 哪天其中某一份醋放多了, 對(duì)象說真難吃, 今天不吃了。這就是單進(jìn)程多線程的模型。一個(gè)菜不好吃導(dǎo)致對(duì)象不吃了(全部線程崩掉)
.. 如果我有兩個(gè)對(duì)象.. 每個(gè)對(duì)象喜歡吃一種菜
ok, 一個(gè)對(duì)象覺得好吃, 吃的臉圓圓的三下巴, 一個(gè)覺得不好吃常年不吃, 骨瘦如柴。
這就是多進(jìn)程單線程互不影響的模型.. 多進(jìn)程多線程我就不舉栗了 ~
說到這里, 小伙伴有沒有對(duì)單進(jìn)程單線程有一些理解呢。nodeJs 就是單進(jìn)程單線程的應(yīng)用程序, 進(jìn)程間互不影響, 綁定多個(gè)事件可以同時(shí)觸發(fā)~ 不用等你完了我再有動(dòng)作, 所以nodeJs性能很高。
nodeJs 單線程類似進(jìn)入一個(gè)while(true ) 的事件循環(huán), 知道沒有事件觀察者的時(shí)候退出(每綁定一個(gè)事件, 就生成一個(gè)事件觀察者), 當(dāng)有事件發(fā)生的時(shí)候, 就會(huì)調(diào)用該事件的回調(diào)函數(shù)。
事件驅(qū)動(dòng)程序
nodeJs 使用事件驅(qū)動(dòng)模型(稍后說), 當(dāng)web server 接收到請(qǐng)求時(shí), 就把它關(guān)閉然后進(jìn)行處理, 然后去服務(wù)下一個(gè)web 請(qǐng)求??梢岳斫獬晌矣|發(fā)事件, 就先關(guān)閉這個(gè)事件驅(qū)動(dòng), 然后處理, 我覺得是在防止二次觸發(fā)~ 造成不正確的負(fù)載和意料之外的結(jié)果,這個(gè)模型非常高效可擴(kuò)展性非常強(qiáng),因?yàn)閣ebserver一直接受請(qǐng)求而不等待任何讀寫操作。(這也被稱之為非阻塞式IO或者事件驅(qū)動(dòng)IO)
可能有的小伙伴會(huì)問了, 什么是 事件驅(qū)動(dòng)模型呢~
其實(shí)在了解事件驅(qū)動(dòng)之前, 我們可以先看一下事件驅(qū)動(dòng)的三大要素:
1) 事件源: 誰來接受外部事件
2) 偵聽器: 能夠接收事件源通知的對(duì)象
3) 事件處理程序: 用于處理事件
好, 包含以上三點(diǎn)的就是一個(gè)完整的事件驅(qū)動(dòng)程序。
舉個(gè)栗子
如果有一天你走在路上一不小心被天上掉下來的花瓶砸到了,并且暈死了過去。那么整個(gè)過程其實(shí)就是一個(gè)事件處理流程,而且我們可以非常方便的分析出剛才所提到的事件驅(qū)動(dòng)模型中的三大要素。
1.被砸暈的這個(gè)人其實(shí)就是事件源,因?yàn)樗悄軌蚪邮艿酵獠康氖录脑大w。
2.偵聽器就是這個(gè)人的大腦神經(jīng),因?yàn)樗鼤?huì)感知到疼痛。
3.事件處理就是這個(gè)人暈死了過去。
在事件驅(qū)動(dòng)模型中,會(huì)生成一個(gè)主循環(huán)來監(jiān)聽事件,當(dāng)檢測(cè)到事件時(shí)觸發(fā)回調(diào)函數(shù)。
整個(gè)事件驅(qū)動(dòng)的流程就是這么實(shí)現(xiàn)的,非常簡(jiǎn)潔。有點(diǎn)類似于觀察者模式,事件相當(dāng)于一個(gè)主題(Subject),而所有注冊(cè)到這個(gè)事件上的處理函數(shù)相當(dāng)于觀察者(Observer)。
說了那么多, 事件綁定怎么寫呢。
在node 里, 有個(gè)事件模塊 events, 我們可以通過實(shí)例化的events 對(duì)象 來綁定事件。
上代碼:
var event = require('events'); // 引入事件模塊 var eventObj = new event(); // 實(shí)例化一個(gè)事件對(duì)象 eventObj.on('起床', function() { // 綁定事件和事件回調(diào) console.log('洗臉?biāo)⒀?); }); eventObj.emit('起床'); // 觸發(fā)事件的方法
結(jié)果是符合預(yù)期的。
但是有一點(diǎn)需要注意哦, 當(dāng)你想移除事件的時(shí)候, 直接eventObj.removeListener(‘起床')
然后各種報(bào)錯(cuò)~
其實(shí)正確的移除事件的方法是這樣的~
上代碼:
var event = require('events'); // 引入事件模塊 var eventObj = new event(); // 實(shí)例化一個(gè)事件對(duì)象 function getUp() { console.log('洗臉?biāo)⒀?); } eventObj.on('起床', getUp); eventObj.emit('起床'); eventObj.removeListener('起床', getUp); eventObj.emit('起床');
成功移除事件~
node 還提供了只觸發(fā)單次事件的方法~
eventObj.once(‘起床', getUp)
還有移除全部事件呀~
eventObj.removeAllListeners(getUp), 像我這樣指定了getUp的事件, 則移除所有觸發(fā)該事件的監(jiān)聽器。
eventObj.setMaxListeners(n) 參數(shù)是一個(gè)數(shù)字, 因?yàn)閚ode默認(rèn)了綁定事件最多10個(gè), 10個(gè)以上的時(shí)候會(huì)警告。 如果不想被警告就設(shè)置最大監(jiān)聽器個(gè)數(shù)咯~
eventObj.listeners(even) 返回指定事件的監(jiān)聽器數(shù)組
eventObj.emit(getUp, [arg1], [arg2], [...])
按照參數(shù)的順序執(zhí)行事件, 返回值是true或者false。有此監(jiān)聽事件就返回true。
不知道小伙伴們有沒有疑惑, 我明明已經(jīng)把events 模塊加載過來了, 為什么還要實(shí)例化才能用呢~
大多數(shù)時(shí)候我們不會(huì)直接使用 EventEmitter,而是在對(duì)象中繼承它。包括 fs、net、 http 在內(nèi)的,只要是支持事件響應(yīng)的核心模塊都是 EventEmitter 的子類。
為什么要這樣做呢?原因有兩點(diǎn):
首先,具有某個(gè)實(shí)體功能的對(duì)象實(shí)現(xiàn)事件符合語義(可以自己命名了啊喂(#`O′) ), 事件的監(jiān)聽和發(fā)射都應(yīng)該是一個(gè)對(duì)象的方法。
其次 JavaScript 的對(duì)象機(jī)制是基于原型的, 支持部分多重繼承,繼承 EventEmitter不會(huì)打亂對(duì)象原有的繼承關(guān)系。
Node 應(yīng)用程序是如何工作的?
在 Node 應(yīng)用程序中,執(zhí)行異步操作的函數(shù)將回調(diào)函數(shù)作為最后一個(gè)參數(shù), 回調(diào)函數(shù)接收錯(cuò)誤對(duì)象作為第一個(gè)參數(shù)。
接下來讓我們來重新看下前面的實(shí)例,創(chuàng)建一個(gè) input.txt ,文件內(nèi)容如下:
123456123123
創(chuàng)建 main.js 文件,代碼如下:
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í)行完畢");
以上程序中 fs.readFile() 是異步函數(shù)用于讀取文件。 如果在讀取文件過程中發(fā)生錯(cuò)誤,錯(cuò)誤 err 對(duì)象就會(huì)輸出錯(cuò)誤信息。
如果沒發(fā)生錯(cuò)誤,readFile 跳過 err 對(duì)象的輸出,文件內(nèi)容就通過回調(diào)函數(shù)輸出。
執(zhí)行以上代碼,執(zhí)行結(jié)果如下:
123456123123
接下來我們刪除 input.txt 文件,執(zhí)行結(jié)果如下所示:
程序執(zhí)行完畢Error: ENOENT, open 'input.txt'
因?yàn)槲募?input.txt 不存在,所以輸出了錯(cuò)誤信息。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)tab欄切換效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)tab欄切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03JS實(shí)現(xiàn)點(diǎn)擊li標(biāo)簽彈出對(duì)應(yīng)的索引功能【案例】
這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊li標(biāo)簽彈出對(duì)應(yīng)的索引功能,結(jié)合具體實(shí)例形式分析了javascript事件響應(yīng)、元素遍歷等相關(guān)操作技巧,需要的朋友可以參考下2019-02-02同步異步動(dòng)態(tài)引入js文件的幾種方法總結(jié)
下面小編就為大家?guī)硪黄疆惒絼?dòng)態(tài)引入js文件的幾種方法總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09javascript實(shí)現(xiàn)簡(jiǎn)易計(jì)算器
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)簡(jiǎn)易計(jì)算器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02javascript四舍五入函數(shù)代碼分享(保留后幾位)
這篇文章主要介紹了javascript四舍五入函數(shù)分享,大家參考使用吧2013-12-12