關於node.js的誤會

昨天寫了篇博客,介紹了一下我對node.js的第一次親密接觸後的感覺,覺得node.js很小衆,出乎我意料不少人感興趣,而且對博客中的細節問題作了評論,最多的是圍繞node.js的異步與單線程展開的,固然還有不少關於node.js到底是不是語言?不是的話又是什麼。。。之類的問題,其實剛接觸node.js,瞭解的並非很深刻,越是回覆你們問題,內心越是沒底,決定認真研究一下,經人指點看了一下《Node.js開發指南》發現大部分問題都有了答案,權當一個讀書筆記把問題答案分享出來,但願能夠幫到一些和我同樣才接觸node.js的小菜html

 關於單線程一個由來已久的誤會

在上篇博客中提到咱們使用node.js寫的JavaScript代碼是單線程運行的,讓不少同窗很疑惑,單線程怎麼實現異步操做,單線程誰去響應事件。。。在html5 Web Workers中我也有提到過客戶端的JavaScript也是單線程運行的,你們明顯沒有這麼大反應,仍是廣泛能接受的。可單線程的客戶端JavaScript也能響應DOM事件,還有你們都很熟悉的ajax操做,回調函數也是異步的,既然客戶端JavaScript是單線程執行的,回調函數是誰調用的呢?答案很簡單,JavaScript的宿主環境——瀏覽器,也就是說雖然JavaScript是單線程執行的,但瀏覽器是多線程的,負責調度管理JavaScript代碼,讓它們在恰當的時機執行。html5

因此咱們所說的node.js單線程,是指node.js並無給咱們建立一個線程的能力,全部咱們本身寫的代碼都是單線程執行的,在同一時間內,只能執行咱們寫的一句代碼。但宿主環境node.js並非單線程的,它會維護一個執行隊列,循環檢測,調度JavaScript線程來執行。所以單線程執行和併發操做並不衝突。node

阻塞與線程

什麼叫阻塞(block)?線程在執行中若是遇到I/O操做(磁盤讀寫、網絡通訊等)一般須要耗費較長的時間,這時候操做系統會剝奪線程對CPU的控制權,使其暫停,並把資源讓給其它的工做線程,這種線程調度方式成爲阻塞。當I/O操做完畢的時候操做系統將這個線程的阻塞狀態解除,恢復其對CPU的控制權,令其繼續執行,這種I/O模式就是同步I/O或成爲阻塞I/O。git

響應的異步I/O或非阻塞I/O則針對全部的I/O操做採起不阻塞的策略,當線程遇到I/O操做時不會以阻塞的方式等待I/O操做結束,而只是將I/O請求發送給操做系統,繼續執行後續語句。當操做系統完成I/O操做時以事件的形式通知執行I/O操做的線程,線程會在特定時間處理這個事件。爲了處理異步I/O必須有事件循環,不斷檢查有沒有未處理的事件,依次予以處理。github

在阻塞模式下,一個線程只能處理一個任務,要想提升吞吐量必須經過多線程。而在非阻塞模式下一個線程永遠在執行計算操做,這個線程所使用的CPU核心利用率永遠是100%,I/O以事件的方式通知。在阻塞模式下多線程每每可以提升系統吞吐量,由於一個線程阻塞時還有其餘線程在工做,多線程何以讓CPU資源不被阻塞的線程浪費。而在非阻塞模式下,線程不會被I/O阻塞,永遠在利用CPU。異步I/O減小了多線程中建立線程、分配內存、列入調度、切換線程、內存換頁、CPU緩存等方面的開銷。web

事件循環機制

上面提到了幾回事件循環機制,那麼這個聽起來貌似很高端的東東到底是什麼呢?所謂事件循環是指node.js會把全部的異步操做使用事件機制解決,有個線程在不斷地循環檢測事件隊列。node.js中全部的邏輯都是事件的回調函數,因此node.js始終在事件循環中,程序入口就是事件循環第一個事件的回調函數。事件的回調函數中可能會發出I/O請求或直接發射( emit)事件,執行完畢後返回事件循環。事件循環會檢查事件隊列中有沒有未處理的事件,直到程序結束。node.js的事件循環對開發者不可見,由libev庫實現,libev不斷檢查是否有活動的、可供檢測的事件監聽器,直到檢查不到時才退出事件循環,程序結束。ajax

node.js是什麼?和JavaScript有什麼關係?

 關於node.js到底是什麼,你們的問題在於數據庫

  1. node.js是否是一門語言?
  2. node.js是否是一個JavaScript庫函數?
  3. node.js是否是一個JavaScript框架?

 很遺憾,這三個問題的答案都是NO,看看官方對本身的描述瀏覽器

Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.緩存

官方很明確地說node.js是一個platform,也就是一個作xxx的平臺。node.js是一個能夠在服務器端運行JavaScript的平臺,其實這句話華也不許確,按照《JavaScript權威指南》《JavaScript高級程序設計》等書中的定義,JavaScript是由兩部分組成

  1. core JavaScript
  2. client JavaScript(DOM、BOM)

而只有core JavaScript能夠在node.js上運行,因此node.js借用了JavaScript的語法,但並不能用來處理瀏覽器對象(BOM)及文檔對象(DOM),因此node.js並非設計爲在服務器端運行解析html文檔的(固然有module能夠作此事),因此「在服務器端運行的JavaScript」在必定程度上誤導了初學者。

同時node.js並不只僅運行core JavaScript,node.js中還有文件系統、模塊包、操做系統API、網絡通訊、二進制類型處理等core JavaScript不具有的功能。

node.js是在執行JavaScript語句嗎

從字面意思上是的,由於咱們在使用node.js開發的時候寫的確實是JavaScript語句,並且node.js利用Google的V8 Javascript 引擎來解析JavaScript語句,但系統真正調用執行的代碼是用C++寫的,咱們作的只是用JavaScript語句來調用這些底層API,因此不用擔憂其執行效率太低問題

異步I/O與事件驅動

 絕不誇張的說node.js最大的特定就是採用異步I/O和事件驅動架構,對於高併發解決方案傳統架構師多線程模型,爲每一個業務邏輯童工一個線程,經過系統線程切換來來彌補同步I/O調用的時間開銷。node.js使用的是單線程模型,對全部I/O都採用異步式的請求方式,避免頻繁的上下文切換,在node.js執行的時候維護着一個事件隊列,程序在執行時進入事件循環等待下一個事件到來,每一個異步I/O請求完成後都會被推送到事件隊列中的等待執行。

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

 res = db.query('SELECT * from some_table');
 res.output();

 

代碼執行到第一行的時候線程會阻塞,等待query返回結果,而後繼續處理。因爲數據庫查詢、磁盤讀寫、網絡通訊等緣由阻塞時間會很是大(相對於CPU始終頻率)。對於高併發的訪問,一方面線程長期阻塞等待,另外一方面爲了應付新情求而不斷添加新線程,會浪費大量系統資源,同時線程的增長也會也會佔用大量的CPU時間來處理內存上下文切換。看看node.js怎麼處理

db.query('SELECT * from some_table', function(res) { 
res.output(); });

 

在代碼中熟悉Javascript的同窗一眼就能夠看明白query的第二個參數是一個回調函數,進程執行到db.query的時候不會等待結果返回,而是直接繼續執行下面的語句,直到進入事件循環。當數據庫執行結果返回的時候會將事件發送到事件隊列,等到線程進入事件循環後纔會調用以前的回調函數。

node.js的異步機制是基於事件的,全部的I/O、網絡通訊、數據庫查詢都以非阻塞的方式執行,返回結果由事件循環來處理。node.js在同一時刻只會處理一個事件,完成後當即進入事件循環檢查後面事件。這樣CPU和內存在同一時間集中處理一件事,同時儘可能讓耗時的I/O等操做並行執行。

node.js架構

node.js用異步式I/O和事件驅動代替多線程提高性能,除了使用高效的V8做爲JavaScript引擎外還使用了高效的libev和libeio庫支持事件驅動和異步I/O。

node.js做者在libeio和libev的基礎上抽象出了libuv層,對於POSIX(Portable Operating System Interface 是一套操做系統API規範,遵循的有Unix、Linux、Mac OS X等)操做系統libuv經過封裝libev和libio來利用epoll或kqueue。而在Windows下libuv使用了IOCP(Input/Output Completion Port,輸入輸出完􏰛端)機制 在不一樣平臺下實現高性能。

瞭解了這個就不要再覺得node.js是利用JavaScript來操做系統了

爲何要使用node.js

其實這個問題能夠歸結爲node.js有什麼特長,除了語法上讓熟悉JavaScript的人很舒服。相對於Javascript爲客戶端而生,node.js爲網絡而生,一切都以http爲主,其內建HTTP服務器支持,使用node.js能夠輕易地搭建一個網站和服務器組合,而不用想使用PHP還須要額外的Apache服務器,經過特有模塊或CGI調用才能將PHP腳本結果返回給用戶。

node.js還能夠部署到非網絡應用環境下,由於其能夠調用C/C++代碼,充分利用現有函數庫,在性能上有很大優越性。

在這些場景下使用node.js是很是合適的

  • web socket服務器
  • TCP/UDP套接字應用程序
  • 複雜邏輯的web應用
  • 命令行工具
  • 客戶端Javascript編譯器

 參考及最後

博客中基本理論知識都來源於《Node.js開發指南》,甚至不少篇幅都是直接使用原話,沒有抄襲據爲己有的意思,只是做者說的太明白了,但願對初入node.js的朋友有幫助,固然若是感興趣能夠直接購買原書。

但願嘮嘮叨叨這麼多,可以對對以前博客持有疑問的博友有所幫助,最後在嘮叨一句,單線程執行是指咱們寫的JavaScript語句在同一時刻只能執行一句,而不是node.js是單線程,其實咱們的異步I/O及事件循環都是另外線程在作。

 固然對於一些CPU密集的操做在node.js裏面也有process.nextTick()這樣的解決方案或者直接使用C++處理,研究明白了和你們分享,也有人不滿意node.js的單線程寫了本身的module來讓node.js多線程,感興趣的同窗能夠看看node-threads-a-gogo

 因爲剛剛接觸node.js,文中謬誤頗多,但願多家多多批評指教

相關文章
相關標籤/搜索