中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

關于真正理解Node.js事件循環(huán)你需要了解的一切

2018-07-20    來源:編程學習網(wǎng)

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

Node.js是一個基于事件的平臺。這意味著Node中發(fā)生的任何事情都是對于事件的響應。傳入Node的數(shù)據(jù)處理要經(jīng)歷一層層嵌套的回調(diào)。這一流程相對于開發(fā)者被抽象出來,由一個叫做libuv的庫處理,就是libuv為我們提供了事件循環(huán)機制。

事件循環(huán)也許是Node中最容易被誤解的概念。

我為Dynatrace工作,這是一家性能監(jiān)控服務商。在我們解決事件循環(huán)監(jiān)控這一問題時,我們付出了很多努力去正確理解我們正在監(jiān)測的部分。

這篇文章將包含我們所學到的,事件循環(huán)是如何工作,以及如何去正確的監(jiān)控它。

常見錯誤觀念

libuv是為Node.js提供事件循環(huán)的庫。libuv背后的關鍵人物之一,Bert Belder,在他令人驚嘆的 Node Interactive的主題演講的一開始,他以一個Google圖片搜索的結(jié)果展示了人們用來描繪事件循環(huán)的不同方法,并且他說其中大部分是錯誤的。

關于真正理解Node.js事件循環(huán)你需要了解的一切 - 眾成翻譯

我來概括一下(在我看來)最普遍的錯誤觀念。

錯誤觀念 1: 事件循環(huán)在用戶代碼中運行于一個獨立的線程。

錯誤觀念

一個主線程來執(zhí)行用戶的JavaScript代碼(用戶代碼 userland code),另一個線程來執(zhí)行事件循環(huán)。每當有異步操作發(fā)生,主線程將會把異步操作移交給事件循環(huán)線程,當異步操作完成,事件循環(huán)線程將會通知主線程去執(zhí)行回調(diào)。

事實

只有一個線程來執(zhí)行JavaScript代碼而且事件循環(huán)也運行在這個線程之中;卣{(diào)(一個運行中的Node.js應用中的任何用戶代碼都是回調(diào))的執(zhí)行通過事件循環(huán)來完成。后面我們將深入了解這些。

錯誤觀念 2: 所有異步操作通過一個線程池來處理

錯誤觀念

異步操作,比如操作文件系統(tǒng),發(fā)起對外的HTTP請求或者數(shù)據(jù)庫交互總是需要加載一個由libuv提供的線程池。

事實

libuv默認創(chuàng)建一個由四個線程組成的線程池來加載異步操作。如今的操作系統(tǒng)已經(jīng)對很多I/O任務提供了異步接口 (如Linux中的AIO)。只要有可能,libuv都會使用這些異步接口而避免使用線程池。這同樣適用于第三方的子系統(tǒng)比如數(shù)據(jù)庫。這些驅(qū)動的作者將更傾向于使用異步接口而不是使用線程池。簡而言之: 只有不存在其他方式的時候,異步I/O才會使用線程池。

錯誤觀念 3: 事件循環(huán)類似于;蜿犃

錯誤觀念

事件循環(huán)輪詢一個由異步任務組成的先進先出隊列,當任務完成時執(zhí)行回調(diào)。

事實

雖然需要類似于隊列的結(jié)構(gòu),但是事件循環(huán)并沒有使用棧。事件循環(huán)就像是一系列的階段以循環(huán)的方式處理各自具體任務的過程。

理解事件循環(huán)中的不同階段

為了真正了解事件循環(huán)我們必須去了解它在每個階段做了哪些工作。希望可以得到Bert Belder的認同,以我的方式來展示事件循環(huán)是如何工作的將會是下面這樣:

關于真正理解Node.js事件循環(huán)你需要了解的一切 - 眾成翻譯

讓我們來聊一聊這些階段。全面的解釋可以在Node.js 網(wǎng)站上看到。

Timers

各種通過setTimeout()或者setInterval()設置的定時任務都將在這一階段被處理。

IO Callbacks

這一階段大多數(shù)回調(diào)會被處理。這里指的是用戶代碼,因為所有在Node.js中的用戶代碼本質(zhì)上來說都在回調(diào)中(例如一個剛收到的http請求會觸發(fā)一連串嵌套的回調(diào))。

IO Polling

輪詢將在下一輪事件循環(huán)中被處理的新事件。

Set Immediate

執(zhí)行所有通過setImmediate()注冊的回調(diào)。

Close

所有偵聽close事件的回調(diào)將在這一階段被處理。

監(jiān)控事件循環(huán)

我們可以看出事實上一個Node應用里發(fā)生的任何事情都是通過事件循環(huán)來運行的。這意味著如果我們可以從事件循環(huán)中得到各種指標,這些指標可以在應用大體上的健康情況和性能方面,為我們提供有價值的信息。由于沒有可以從事件循環(huán)中獲取到運行時指標的API,各種監(jiān)控工具提供了各自的指標。來看一下我們所提供的指標。

Tick Frequency

每段時間內(nèi)完成的周期數(shù)量。

Tick Duration

一個周期需要花費的時間。

由于我們的代理可以像原生模塊那樣運行,通過添加探針來為我們提供這些信息是相對容易的。

Tick frequency 和 tick duration 指標在實際中的應用

當我們第一次在不同的負載在進行測試的時候,結(jié)果是令人意想不到的----讓我展示一個示例:

在下面的場景中,我將調(diào)用一個express.js應用來向另外一臺http服務器發(fā)送請求。

這里有四個場景:

  1. Idle 沒有收到任何請求。

  2. ab -c 5 利用apache bench一次創(chuàng)建5個并發(fā)請求

  3. ab -c 10 10個并發(fā)請求

  4. ab -c 10 (slow backend) http服務器1s后再返回數(shù)據(jù)來模擬緩慢的后端。這會產(chǎn)生回調(diào)的壓力因為請求在等待的后端返回在Node內(nèi)部堆積。

關于真正理解Node.js事件循環(huán)你需要了解的一切 - 眾成翻譯

如果我們觀察結(jié)果圖表,我們可以得出一個有趣的結(jié)論:

事件循環(huán)的持續(xù)時間和頻率是動態(tài)的,以適應負載的變化。

如果應用是空閑的,意味著沒有待處理的任務(計時器任務或是回調(diào)等等),因為沒有理由去全速完成事件循環(huán)中的各個階段,因此事件循環(huán)會調(diào)整以適應這一情況,并且會在輪詢階段阻塞一會兒來等待新的外部事件進來。

這也意味著,沒有負載下的指標(低頻率高耗時)與在高負載下緩慢的后端的情況下的指標是相似的。

我們也看到這個示例應用在5個并發(fā)請求的場景下運行的狀態(tài)最好。

因此周期頻率和周期時間應以當前的每秒請求數(shù)為基準。

盡管這些數(shù)據(jù)已經(jīng)為我們提供了一些有價值的信息,但我們依舊不知道時間花在哪一個階段,因此我們做了更加深入的研究,又提出了兩個新指標。

Work processed latency

這個指標用了度量一個異步任務被線程池處理所花費的時間。

高的工作處理時延表明了這是一個忙碌/被耗盡的線程池。

為了測試這個指標,我創(chuàng)建了一個express路由,利用一個名叫Sharp的圖片來處理圖片。因為圖片處理是昂貴的,Sharp利用線程池來完成對圖片的處理。

關于真正理解Node.js事件循環(huán)你需要了解的一切 - 眾成翻譯

運行Apache bench以5個并發(fā)連接請求有圖片處理功能的路由的結(jié)果直接的反映在這個圖表上,并且能夠很明顯的與中等負載而無圖片處理的場景區(qū)分開。

Event Loop Latency

事件循環(huán)時延用來度量一個通過setTimeout(X)設置的定時任務被處理所花費的時間。

高的時間循環(huán)時延意味著時間循環(huán)忙于處理回調(diào)。

為了這次這個指標,我創(chuàng)建了一個express路由,通過一個很低效的算法來計算斐波那契數(shù)列。

關于真正理解Node.js事件循環(huán)你需要了解的一切 - 眾成翻譯

運行Apache bench,以5個并發(fā)連接調(diào)用有斐波那契數(shù)列計算功能的路由,結(jié)果展示了當前回調(diào)隊列是忙碌的。

我們清楚地看到上面四個指標可以為我們提供有價值的信息來幫助我們更好的理解Node.js的內(nèi)部是如何工作。

所有這些指標都需要從一個更大的圖景來觀察以理解它。因此我們當前正在收集信息并將這些數(shù)據(jù)作為參考因素。

調(diào)整事件循環(huán)

事實上,僅有指標而不知道如何采取行動去修正這些問題對我們幫助不大。這里有一些關于事件循環(huán)看起來繁忙時應該如何去做的建議。

關于真正理解Node.js事件循環(huán)你需要了解的一切 - 眾成翻譯

利用所有的CPU

一個Node.js應用運行在一個單一的線程中。這意味著在多核設備中,負載并沒有被分發(fā)到所有的核心上。使用 cluster模塊,它使得Node.js可以輕松的在每個CPU上創(chuàng)建子進程。每個子進程維護著一個獨立的事件循環(huán),并且主進程將負載分發(fā)到所有的子進程中。

調(diào)整線程池

就像上面提到的,libuv將創(chuàng)建一個四個線程的線程池。這個線程池的默認大小可以通過設置環(huán)境變量UV_THREADPOOL_SIZE來重寫。雖然這樣可以解決I/O密集型應用的負載問題,但是過高的負載測試例如過大的線程池依舊會耗盡內(nèi)存或CPU的資源。

移除服務中的計算密集型工作

如果Node.js花費太多時間在計算密集型操作上,為服務移除這些工作或是使用另一種更適合這個任務的語言將會是一個切實可行的選擇。

總結(jié)

讓我們總結(jié)一下在這篇文章中我們學到的:

  • 事件循環(huán)維持著一個Node.js應用的運行

  • 它的功能經(jīng)常被錯誤的理解----它是需要經(jīng)歷一系列的階段,每個階段處理不同的任務

  • 事件循環(huán)沒有提供開箱可用的指標,因此不同的APM服務商收集的指標是不同的。

  • 雖然這些指標提供了關于性能瓶頸有價值的信息,但是深入理解事件循環(huán)機制和正在執(zhí)行的代碼才是關鍵。

  • 在未來,Dynatrace將增加一個事件循環(huán)遠程監(jiān)控技術到根本原因檢測中以將事件循環(huán)的異常與問題相關聯(lián)。

對我來說,毫無疑問的我們剛剛創(chuàng)建了當今市場上最全面的事件循環(huán)監(jiān)控解決方案,并且我很開心這些令人激動的新特性將在接下來的幾周內(nèi)推向我們的用戶。

感謝

Dynatrace中杰出的Node.js代理團隊在事件循環(huán)監(jiān)控上付出了很多努力。這篇博客文章中呈現(xiàn)的大部分發(fā)現(xiàn)是基于他們在Node.js內(nèi)部工作機制方面深入的知識。我想感謝Bernhard Liedl、Dominik Gruber、Gerhard St?bich和Gernot Reisinger,感謝他們付出的努力以及對我的支持。

我希望這篇文章在這個主題上對讀者確實有所啟發(fā)。請關注我的twitter@dkhan,在很高興在那里或是在下面的評論區(qū)里解答你們的提問。

如果你想繼續(xù)了解更多事件循環(huán)的內(nèi)部工作機制或是作為開發(fā)者如何使用事件循環(huán),我推薦我朋友發(fā)表在RisingStack上的這篇文章 。

如果你想嘗試一下我們的Node.js監(jiān)控,下載我們的免費試用版并在任何時間分享你的反饋給我——這是我們了解用戶的方式。

來自:

 

標簽: Google http服務器 linux 代碼 服務器 服務商 開發(fā)者 數(shù)據(jù)庫 搜索

版權申明:本站文章部分自網(wǎng)絡,如有侵權,請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:2018 年初值得關注的 25 個新 Android 庫和項目

下一篇:Go 1.10中值得關注的幾個變化