Node.js機制及原理理解初步

jopen 10年前發布 | 25K 次閱讀 Node.js Node.js 開發

一、node.js優缺點

node.js是單線程。

好處就是

1)簡單

2)高性能,避免了頻繁的線程切換開銷

3)占用資源小,因為是單線程,在大負荷情況下,對內存占用仍然很低

3)線程安全,沒有加鎖、解鎖、死鎖這些問題

php

20141216195618405.png

node.js

20141216195721445.png

壞處就是

如何解決高并發?

node使用異步IO和事件驅動(回調函數)來解決這個問題。

一般來說,高并發解決方案會提供多線程模型,為每個業務邏輯提供一個線程,通過系統線程切換來來彌補同步I/O調用的時間開銷。像apache,是一個請求一個線程。

而node.js使用的是單線程模型,對所有I/O都采用異步的請求方式,避免頻繁的上下文切換,在node.js執行的時候維護著一個事件隊列;程序在執行時進入事件循環等待下一個事件到來,每個異步I/O請求完成后都會被推送到事件隊列中的等待執行。

比如說:

對于一個簡單的數據庫訪問操作,傳統方式是這樣實現的

    res = db.query('SELECT * from some_table');  
    res.output();  
</div>

    </div> 代 碼執行到第一行的時候線程會阻塞,等待query返回結果,然后繼續處理。由于數據庫查詢、磁盤讀寫、網絡通信等原因(所謂的I/O)阻塞時間會非常大 (相對于CPU始終頻率)。對于高并發的訪問,一方面線程長期阻塞等待,另一方面為了應付新情求而不斷添加新線程,會浪費大量系統資源,同時線程的增加也 會也會占用大量的CPU時間來處理內存上下文切換。看看node.js怎么處理
    db.query('SELECT * from some_table', function(res) {   
       res.output();  
    });  

    query的第二個參數是一個回調函數,進程執行到db.query的時候不會等待結果返回,而是直接繼續執行下面的語句,直到進入事件循環。當數據庫執行結果返回的時候會將事件發送到事件隊列,等到線程進入事件循環后才會調用之前的回調函數。

    node.js 的異步機制是基于事件的,所有的I/O、網絡通信、數據庫查詢都以非阻塞的方式執行,返回結果由事件循環來處理。node.js在同一時刻只會處理一個事 件,完成后立即進入事件循環檢查后面事件。這樣CPU和內存在同一時間集中處理一件事,同時盡量讓耗時的I/O等操作并行執行。

    事件循環機制

    所謂事件循環是指node.js會把所有的異步操作使用事件機制解決,有個線程在不斷地循環檢測事件隊列。

    node.js 中所有的邏輯都是事件的回調函數,所以node.js始終在事件循環中,程序入口就是事件循環第一個事件的回調函數。事件的回調函數中可能會發出I/O請 求或直接發射( emit)事件,執行完畢后返回事件循環。事件循環會檢查事件隊列中有沒有未處理的事件,直到程序結束。node.js的事件循環對開發者不可見,由 libev庫實現,libev不斷檢查是否有活動的、可供檢測的事件監聽器,直到檢查不到時才退出事件循環,程序結束。

    如圖所示

    20141216194247656.png

     libuv 是一個高性能事件驅動的程序庫,封裝了 Windows 和 Unix 平臺一些底層特性,為開發者提供了統一的 API.
    因此,node.js 是單線程,異步非阻塞。

    但畢竟,如何彌補單線程缺陷?是不是有異步非阻塞,就可以高枕無憂了?

    不是的。

    1)CPU密集型任務存在短板

    如 上所述,nodejs的機制是單線程,這個線程里面,有一個事件循環機制,處理所有的請求。如圖所示。在事件處理過程中,它會智能地將一些涉及到IO、網 絡通信等耗時比較長的操作,交由worker threads去執行,執行完了再回調,這就是所謂的異步IO非阻塞吧。但是,那些非IO操作,只用CPU計算的操作,它就自己扛了,比如算什么斐波那契 數列之類。它是單線程,這些自己扛的任務要一個接著一個地完成,前面那個沒完成,后面的只能干等。因此,對CPU要求比較高的CPU密集型任務多的話,就 有可能會造成號稱高性能,適合高并發的node.js服務器反應緩慢。

    20141216195736812.png

    20141216200721188.png

    2)無法利用CPU的多核

    最 開始,線程只是用于分配單個處理器處理時間的一種機制。但假如操作系統本身支持多個CPU/內核,那么每個線程都可以得到一個不同自己的CPU/內核,實 現真正的“并行運算”。在這種情況下,多線程程序可以提高資源使用效率。Node.js是單線程程序,它只有一個event loop,也只占用一個CPU/內核。現在大部分服務器都是多CPU或多核的,當Node.js程序的event loop被CPU密集型的任務占用,導致有其它任務被阻塞時,卻還有CPU/內核處于閑置的狀態,造成資源的浪費。

    解決方案

    利用原生模塊或第三方模塊,開辟進程或子進程,用于處理這些特殊的任務。

    3)如果有異常拋出,因為是單線程,整個項目將不可用。</span>但這歸根到底是代碼的問題,糟糕的代碼,不管什么體系,都會有問題,即使不崩潰。解決辦法是用pm2等工具來運行?

    二、nodejs與javascript的關系

    nodejs本身不是開發語言,它是一個工具或者平臺,在服務器端解釋、運行javascript;coffeescript屬于nodejs體系,算是一種新的開發語言,但它的目的在于最后編譯成javascript。

    nodejs利用Google V8來解釋運行javascript,但是系統真正執行的代碼是用C++寫的。javascript做的只是調用這些API而已。因此,并無執行效率的問題。

    20141216195846231.png

    三、nodejs適用場景


    1、RESTful API

    這是適合 Node 的理想情況,因為您可以構建它來處理數萬條連接。它仍然不需要大量邏輯;它本質上只是從某個數據庫中查找一些值并將它們組成一個響應。由于響應是少量文本,入站請求也是少量的文本,因此流量不高,一臺機器甚至也可以處理最繁忙的公司的 API 需求。

    2、實時程序

    比如聊天服務

    聊天應用程序是最能體現 Node.js 優點的例子:輕量級、高流量并且能良好的應對跨平臺設備上運行密集型數據(雖然計算能力低)。同時,聊天也是一個非常值得學習的用例,因為它很簡單,并且涵蓋了目前為止一個典型的 Node.js 會用到的大部分解決方案。

    3、單頁APP

    ajax很多。現在單頁的機制似乎很流行,比如phonegap做出來的APP,一個頁面包打天下的例子比比皆是。

    。。。

    總而言之,NodeJS適合運用在高并發、I/O密集、少量業務邏輯的場景



    參考文章

    關于node.js的誤會

    Node.js的線程和進程

    http://www.slideshare.net/mysqlops/nodejs-9313477

    Node.js軟肋之CPU密集型任務

    http://www.ruanyifeng.com/blog/2014/10/event-loop.html

    http://segmentfault.com/a/1190000000375619

    http://www.cnblogs.com/sysuys/p/3460614.html


    來自:http://blog.csdn.net/leftfist/article/details/41891407

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!