node.js為什麼能處理高併發情景?

杜嘉偉發表於2019-03-04

什麼叫做高併發場景?

高併發場景指數量龐大的操作或請求在同一時間段內發生,列如:雙11當晚,龐大的使用者在晚上十二點時間段訪問淘寶,淘寶的伺服器需要在短時間內處理大量的網路請求。

高併發場景所帶來的問題

高併發場景會讓伺服器在短時間內多次進行i/o操作,如取得硬碟上中儲存的關於使用者的相關資訊。i/o操作不是需要cpu來完成,而是通過DMA,例如:一個淘寶使用者訪問淘寶伺服器,cpu這時處理這個請求,cpu和DMA可能會產生這樣的對話:

  • CPU:'嘿,朋友,我這邊需要拿個資料,你能幫我拿一下嗎?'
  • DMA:'好嘞,稍等一會。'
  • 經過等待之後
  • DMA:'你要的東西我幫你拿回來了'
  • CPU:'太棒了,謝謝~'
  • 由上可見,cpu需要等待DMA獲取資料,在拿到資料後才進行之後的相應操作,這樣CPU等待的時間就白白浪費掉了,也就是說其他需要cpu做得工作也會延因為CPU的等待而延遲了,試想,在高併發環境伺服器的cpu對每一個客戶的請求都等待一段時間,這樣晚到達的請求可能雙十一結束了還沒有看到淘寶的頁面. 如何解決呢?
  • CPU可以僱一個高階專家叫做‘程式’來幫他接受使用者的請求並等待資料的獲取,好比,cpu說‘這是我分配給你的資源,你拿了資源,就好好地從這邊等著,DMA資料拿完了告訴我一聲’,程式拿了資源就要做事,這樣CPU就不用等待了,CPU可以處理其他的工作,等到程式通知它拿到資料之後,他就可以做和資料有關的一些操作了。
  • cpu之後發現,僱程式這一個高階專家太昂貴了,例如,每一個客戶等請求都需要建立一個程式來服務,如果有100個使用者,那麼就需要100個程式,cpu通過程式複製來進行建立程式,那麼這樣每建立一個程式,就要複製相應的內部狀態,導致相同的資料在記憶體中存在多分,造成浪費。怎麼辦呢?
  • cpu發現,我可以不用請高階專家來做接受請求並等待資料這樣一件小事情,請幾個小朋友就能把事情給做了,於是CPU請來了執行緒,和程式相比,用執行緒來等待資料有什麼優勢呢?在建立執行緒的過程中,所有建立的執行緒利用的都是同一份資源,因為‘程式是資源分配的最小單元’---這句話教作業系統的老師幾乎都會說一遍,這樣就避免了資源浪費。才用執行緒的複製也只能說比程式的複製好,因為,1.使用者請求時,需要切到執行緒2.當執行緒接受請求並等待資料後,需要CPU切換到當前程式把資料傳回到cpu所在單執行緒,這也就出現了切換執行上下文---可以理解為程式執行時所需要的資源環境 所造成的浪費,加之建立、銷燬執行緒所需要的資源。當請求過多時,切換上下文所造成的浪費會佔據超多的時間。那麼怎麼辦呢?那就出現了node所採用的基於事件驅動的非阻塞非同步i/o。
  • node是怎麼做的呢? node的主執行緒來處理使用者的請求封裝請求物件,利用自身底部的執行緒池*進行請求物件中的I/O操作,等到資料後歸還執行緒,並將資料給主執行緒中的I/O觀察者,cpu主執行緒從觀察者取出回撥函式執行。 與執行緒建立相比,我不用主動建立執行緒,也不用利用執行緒去處理使用者請求,這樣就減少了不必要的建立執行緒的和切換執行上下文的浪費。

注:node的事件迴圈,觀察者,請求物件的概念都沒有進行介紹,請參考《node.js深入淺出》。

參考資料:《計算機作業系統(第四版)》---西安電子科技大學出版社 《node.js深入淺出》---樸靈

相關文章