久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      聊聊Node中的異步實現(xiàn)與事件驅(qū)動

      本篇文章帶大家了解一下Node中的異步實現(xiàn)與事件驅(qū)動,希望對大家有所幫助!

      聊聊Node中的異步實現(xiàn)與事件驅(qū)動

      node.js極速入門課程:進(jìn)入學(xué)習(xí)

      Node的特點

      計算機中的一些任務(wù)一般可以劃分為兩個類別,一個類別叫做IO密集型,一個叫做計算密集型;對于計算密集型的任務(wù),只能不斷榨干CPU的性能,但是對于IO密集型的任務(wù)來說,理想情況下卻并不需要,只需要通知IO設(shè)備進(jìn)行處理,過一段時間再來拿去數(shù)據(jù)就好了?!鞠嚓P(guān)教程推薦:nodejs視頻教程 、編程視頻】

      對于某些場景有一些互不相關(guān)的任務(wù)需要完成,現(xiàn)行的主流方法有如下兩種:

      • 多線程并行完成:多線程的代價在于創(chuàng)建線程和執(zhí)行線程上下文切換的開銷較大。另外,在復(fù)雜的業(yè)務(wù)中,多線程編程經(jīng)常面臨鎖、狀態(tài)同步等問題;
      • 單線程順序執(zhí)行:易于表達(dá),但串行執(zhí)行的缺點在于性能,任意一個略慢的任務(wù)都會導(dǎo)致后續(xù)代碼被組設(shè)

      node在兩者之前給出了它的方案:利用單線程,遠(yuǎn)離多線程死鎖、狀態(tài)同步等問題;利用異步IO,讓單線程遠(yuǎn)離阻塞,以更好地使用CPU

      聊聊Node中的異步實現(xiàn)與事件驅(qū)動

      Node是如何實現(xiàn)異步的

      剛才講了node在多任務(wù)處理的方案,但是node內(nèi)部想要實現(xiàn)卻并不容易,下面介紹操作系統(tǒng)的幾個概念,方面后續(xù)大家更好理解,后面再講一講異步的實現(xiàn)以及node的事件循環(huán)機制:

      阻塞IO與非阻塞IO

      • 阻塞IO:應(yīng)用層面發(fā)起IO調(diào)用之后,就一直等待數(shù)據(jù),等操作系統(tǒng)內(nèi)核層面完成所有操作后,調(diào)用才結(jié)束;

      操作系統(tǒng)中一切皆文件,輸入輸出設(shè)備同樣被抽象為了文件,內(nèi)核在執(zhí)行IO操作時,通過文件描述符進(jìn)行管理

      • 非阻塞IO:差別為調(diào)用后立即返回一個文件描述符,并不等待,這時候CPU的時間片就可以用來處理其他事務(wù),之后可以通過這個文件描述符進(jìn)行結(jié)果的獲取;

      非阻塞IO存在的一些問題:雖然其讓CPU的利用率提高了,但是由于立即返回的是一個文件描述符,我們并不知道IO操作什么時候完成,為了確認(rèn)狀態(tài)變更,我們只能作輪詢操作

      不同的輪詢方法

      • read :最原始、性能最低的一種,通過重復(fù)檢查IO狀態(tài)來完成完整數(shù)據(jù)的獲取
      • select:通過對文件描述符上的事件狀態(tài)來進(jìn)行判斷,相對來說消耗更少;缺點就是它采用了一個1024長度的數(shù)組來存儲狀態(tài),所以它最多可以同時檢查1024個文件描述符
      • poll:由于select的限制,poll改進(jìn)為鏈表的存儲方式,其他的基本都一致;但是當(dāng)文件描述符較多的時候,它的性能還是非常低下的
      • eopll:該方案是linux下效率最高的IO事件通知機制,在進(jìn)入輪詢的時候如果沒有檢查IO事件,將會進(jìn)行休眠,直到事件發(fā)生將它喚醒
      • kqueue:與epoll類似,不過僅在FreeBSD系統(tǒng)下存在

      盡管epoll利用了事件來降低對CPU的耗用,但休眠期間CPU幾乎是閑置的;我們期待的異步IO應(yīng)該是應(yīng)用程序發(fā)起非阻塞調(diào)用,無須通過遍歷或事件喚醒等方式輪詢,可以直接處理下一個任務(wù),只需IO完成后通過信號或者回調(diào)將數(shù)據(jù)傳遞給應(yīng)用程序即可。

      linux下還有中AIO方式就是通過信號或回調(diào)來傳遞數(shù)據(jù)的,不過只有Linux有,并且有限制無法利用系統(tǒng)緩存

      node中對于異步IO的實現(xiàn)

      先說結(jié)論,node對異步IO的實現(xiàn)是通過多線程實現(xiàn)的。可能會混淆的地方就是node內(nèi)部雖然是多線程的,但是我們程序員開發(fā)的JavaScript代碼卻僅僅是運行在單線程上的。

      node通過部分線程進(jìn)行阻塞IO或者非阻塞IO加上輪詢技術(shù)來完成數(shù)據(jù)獲取,讓一個線程進(jìn)行計算處理,通過線程之間的通信將IO得到的數(shù)據(jù)進(jìn)行傳遞,這就輕松實現(xiàn)了異步IO的模擬。

      聊聊Node中的異步實現(xiàn)與事件驅(qū)動

      除了異步IO,計算機中的其他資源也適用,因為linux中一切皆文件,磁盤、硬件、套接字等幾乎所有計算機資源都被抽象為了文件,接下來介紹對計算機資源的調(diào)用都以IO為例子。

      事件循環(huán)

      在進(jìn)程啟動時,node便會創(chuàng)建一個類似與while(true)的循環(huán),每執(zhí)行一次循環(huán)體的過程我們成為Tick;

      下方為node中事件循環(huán)流程圖:

      聊聊Node中的異步實現(xiàn)與事件驅(qū)動

      很簡單的一張圖,簡單解釋一下:就是每次都從IO觀察者里面獲取執(zhí)行完成的事件(是個請求對象,簡單理解就是包含了請求中產(chǎn)生的一些數(shù)據(jù)),然后沒有回調(diào)函數(shù)的話就繼續(xù)取出下一個事件(請求對象),有回調(diào)就執(zhí)行回調(diào)函數(shù)

      異步IO細(xì)節(jié)

      聊聊Node中的異步實現(xiàn)與事件驅(qū)動

      注:不同平臺有不同的細(xì)節(jié)實現(xiàn),這張圖隱藏了相關(guān)平臺兼容細(xì)節(jié),比如windows下使用IOCP中的PostQueuedCompletionStatus()提交執(zhí)行狀態(tài),通過GetQueuedCompletionStatus獲取執(zhí)行完成的請求,并且IOCP內(nèi)部實現(xiàn)了線程池的細(xì)節(jié),而linux等平臺通過eopll實現(xiàn)這個過程,并在libuv下自實現(xiàn)了線程池

      setTimtoutsetInterval

      除了IO等計算機資源需要異步調(diào)用之外,node本身還存在一些與異步IO無關(guān)的一些其他異步API

      • setTimeout
      • setInterval
      • setImmediate
      • process.nextTick

      該小節(jié)先講解前面兩個api

      它們的實現(xiàn)原理與異步IO比較類似,只是不需要IO線程池的參與

      • setTimtoutsetInterval創(chuàng)建的定時器會被插入到定時器觀察者內(nèi)部的一個紅黑樹中
      • 每次tick執(zhí)行的時候,會從該紅黑樹中迭代取出定時器對象,檢查是否超過定時時間
      • 如果超過,就將這個事件(請求對象)推入到事件隊列中,在事件循環(huán)中執(zhí)行其中的回調(diào)函數(shù)

      紅黑樹:這里簡單提一下,就是一種特殊化的平衡二叉樹,可以自平衡,查找效率基本上就是該二叉樹的深度了

      O(log2n)O(log_2n)

      你有考慮過這個問題嗎,為什么定時器不需要線程池的參與了呢,如果你理解了之前章節(jié)對于異步IO實現(xiàn)原理的話,相信你應(yīng)該能解釋出來,這里簡單說說原因來加深記憶:

      node中的IO線程池是用來調(diào)用IO并等待數(shù)據(jù)返回(看具體實現(xiàn))的一種方式,它使JavaScript單線程得以異步調(diào)用IO,并且不需要等待IO執(zhí)行完成(因為是IO線程池做了),并且能獲取到最終的數(shù)據(jù)(通過觀察者模式:IO觀察者從線程池獲取執(zhí)行完成的事件,事件循環(huán)機制執(zhí)行后續(xù)的回調(diào)函數(shù))

      上述這段話可能有點簡略,如果你還不明白,可以看下之前的那幾種圖~

      process.nextTicksetImmediate

      這兩個函數(shù)都是代表立即異步執(zhí)行一個函數(shù),那為什么不用setTimeout(() => { ... }, 0)來完成呢?

      • 定時器精度不夠
      • 定時器使用紅黑樹來創(chuàng)建定時器對象和迭代操作,浪費性能
      • process.nextTick更加輕量

      輕量具體來說:我們在每次調(diào)用process.nextTick的時候,只會將回調(diào)函數(shù)放入隊列中,在下一輪Tick時取出執(zhí)行。定時器中采用紅黑樹的方式時

      O(log2n)O(log_2n)

      ,nextTick

      O(1)O(1)

      process.nextTicksetImmediate又有什么區(qū)別呢?畢竟它們都是將回調(diào)函數(shù)立即異步執(zhí)行

      • process.nextTick的回調(diào)執(zhí)行優(yōu)先級高于setImmediate
      • process.nextTick的回調(diào)函數(shù)保存在一個數(shù)組中,每輪事件循環(huán)下全部執(zhí)行,setImmediate的結(jié)果則是保存在鏈表中,每輪循環(huán)按序執(zhí)行第一個回調(diào)

      注意:之所以process.nextTick的回調(diào)執(zhí)行優(yōu)先級高于setImmediate,因為事件循環(huán)對觀察者的檢查是有順序的,process.nextTick屬于idle觀察者,setImmediate屬于check觀察者。iedl觀察者 > IO 觀察者 > check觀察者

      高性能服務(wù)器

      對于網(wǎng)絡(luò)套接字的處理,node也應(yīng)用到了異步IO,網(wǎng)絡(luò)套接字上偵聽到的請求都會形成事件交給IO觀察者,事件循環(huán)會不停地處理這些網(wǎng)絡(luò)IO事件,如果我們在JavaScrpt層面上有傳入對應(yīng)的回調(diào)函數(shù),這些回調(diào)函數(shù)就會在事件循環(huán)中執(zhí)行(處理這些網(wǎng)絡(luò)請求)

      常見的服務(wù)器模型:

      • 同步式
      • 每進(jìn)程–>每請求
      • 每線程–>每請求

      node采用的是事件驅(qū)動的方式處理這些請求,無需對每個請求創(chuàng)建額外的對應(yīng)線程,可以省略掉創(chuàng)建線程和銷毀線程的開銷,同時操作系統(tǒng)的調(diào)度任務(wù)因為線程較少(只有node內(nèi)部實現(xiàn)的一些線程)上下文切換的代價很低。

      經(jīng)典問題–雪崩問題的解決:

      問題描述:服務(wù)器在剛啟動時,緩存無數(shù)據(jù),如果訪問量巨大,同一條SQL會被發(fā)送到數(shù)據(jù)庫中反復(fù)查詢,影響性能。

      解決方案:

      const proxy = new events.EventEmitter(); let status = "ready"; // 狀態(tài)鎖,避免反復(fù)查詢  const select = function(callback) {     proxy.once("selected", callback);  // 綁定一個只執(zhí)行一次名為selected的事件     if(status === "ready") {         status = "pending";         // sql         db.select("SQL", (res) => {             proxy.emit("selected", res); // 觸發(fā)事件,返回查詢數(shù)據(jù)             status = "ready";         })     } }
      登錄后復(fù)制

      使用once將所有請求的回調(diào)都壓入了事件隊列中,利用其只執(zhí)行一次就會將監(jiān)視器移除的特點,保證每一個回調(diào)函數(shù)只會被執(zhí)行一次。對于相同的SQL語句,保證在同一個查詢開始到結(jié)束的過程中永遠(yuǎn)只有一次。新到來的相同調(diào)用只需在隊列中等待數(shù)據(jù)就緒即可,一旦查詢到結(jié)果,得到的結(jié)果就可以被這些調(diào)用共同使用。

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號