什麼是Node.js

Node.js從2009年誕生至今,已經發展了兩年有餘,其成長的速度有目共睹。從在github的訪問量超過Rails,到去年末Node.jsS創始人Ryan Dalh加盟Joyent得到企業資助,再到今年發佈Windows移植版本,Node.js的前景得到了技術社區的確定。InfoQ一直在關注Node.js的發展,在今年的兩次Qcon大會(北京站和杭州站)都有專門的講座。爲了更好地促進Node.js在國內的技術推廣,咱們決定開設「深刻淺出Node.js」專欄,邀請來自Node.js領域的佈道師、開發人員、技術專家來說述Node.js的各方面內容,讓讀者對Node.js有更深刻的瞭解,而且可以積極投入到新技術的討論和實踐中。javascript

專欄的第一篇文章《什麼是Node.js》嘗試從各個角度來闡述Node.js的基本概念、發展歷史、優點等,對該領域不熟悉的開發人員能夠經過本文了解Node.js的一些基礎知識。前端

從名字提及有關

Node.js的技術報道愈來愈多,Node.js的寫法也是五花八門,有寫成NodeJS的,有寫成Nodejs的,到底哪種寫法最標準呢,咱們不妨遵循官方的說法。在Node.js的官方網站上,一直將其項目稱之爲」Node「或者」Node.js「,沒有發現其餘的說法,」Node「用的最多,考慮到Node這個單詞的意思和用途太普遍,容易讓開發人員誤解,咱們採用了第二種稱呼——」Node.js「,js的後綴點出了Node項目的本意,其餘的名稱五花八門,沒有確切的出處,咱們不推薦使用。java

Node.js不是JS應用、而是JS運行平臺

看到Node.js這個名字,初學者可能會誤覺得這是一個Javascript應用,事實上,Node.js採用C++語言編寫而成,是一個Javascript的運行環境。爲何採用C++語言呢?據Node.js創始人Ryan Dahl回憶,他最初但願採用Ruby來寫Node.js,可是後來發現Ruby虛擬機的性能不能知足他的要求,後來他嘗試採用V8引擎,因此選擇了C++語言。既然不是Javascript應用,爲什麼叫.js呢?由於Node.js是一個Javascript的運行環境。提到Javascript,你們首先想到的是平常使用的瀏覽器,現代瀏覽器包含了各類組件,包括渲染引擎、Javascript引擎等,其中Javascript引擎負責解釋執行網頁中的Javascript代碼。做爲Web前端最重要的語言之一,Javascript一直是前端工程師的專利。不過,Node.js是一個後端的Javascript運行環境(支持的系統包括*nux、Windows),這意味着你能夠編寫系統級或者服務器端的Javascript代碼,交給Node.js來解釋執行,簡單的命令相似於:node

#node helloworld.jsNode.js

採用了Google Chrome瀏覽器的V8引擎,性能很好,同時還提供了不少系統級的API,如文件操做、網絡編程等。瀏覽器端的Javascript代碼在運行時會受到各類安全性的限制,對客戶系統的操做有限。相比之下,Node.js則是一個全面的後臺運行時,爲Javascript提供了其餘語言可以實現的許多功能。nginx

Node.js採用事件驅動、異步編程,爲網絡服務而設計

事件驅動這個詞並不陌生,在某些傳統語言的網絡編程中,咱們會用到回調函數,好比當socket資源達到某種狀態時,註冊的回調函數就會執行。Node.js的設計思想中以事件驅動爲核心,它提供的絕大多數API都是基於事件的、異步的風格。以Net模塊爲例,其中的net.Socket對象就有如下事件:connect、data、end、timeout、drain、error、close等,使用Node.js的開發人員須要根據本身的業務邏輯註冊相應的回調函數。這些回調函數都是異步執行的,這意味着雖然在代碼結構中,這些函數看似是依次註冊的,可是它們並不依賴於自身出現的順序,而是等待相應的事件觸發。事件驅動、異步編程的設計(感興趣的讀者能夠查閱筆者的另外一篇文章《Node.js的異步編程風格》),重要的優點在於,充分利用了系統資源,執行代碼無須阻塞等待某種操做完成,有限的資源能夠用於其餘的任務。此類設計很是適合於後端的網絡服務編程,Node.js的目標也在於此。在服務器開發中,併發的請求處理是個大問題,阻塞式的函數會致使資源浪費和時間延遲。經過事件註冊、異步函數,開發人員能夠提升資源的利用率,性能也會改善。
從Node.js提供的支持模塊中,咱們能夠看到包括文件操做在內的許多函數都是異步執行的,這和傳統語言存在區別,並且爲了方便服務器開發,Node.js的網絡模塊特別多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等,開發人員能夠在此基礎上快速構建Web服務器。以簡單的helloworld.js爲例:git

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(80, "127.0.0.1");

上面的代碼搭建了一個簡單的http服務器(運行示例部署在http://helloworld.cnodejs.net/中,讀者能夠訪問),在本地監聽80端口,對於任意的http請求,服務器都返回一個頭部狀態碼爲200、Content-Type'值爲text/plain'的」Hello World「文字響應。從這個小例子中,咱們能夠看出幾點:github

  • Node.js的網絡編程比較便利,提供的模塊(在這裏是http)開放了容易上手的API接口,短短几行代碼就能夠構建服務器。數據庫

  • 體現了事件驅動、異步編程,在createServer函數的參數中指定了一個回調函數(採用Javascript的匿名函數實現),當有http請求發送過來時,Node.js就會調用該回調函數來處理請求並響應。固然,這個例子相對簡單,沒有太多的事件註冊,在之後的文章中讀者會看到更多的實際例子。編程

Node.js的特色

下面咱們來講說Node.js的特色。事件驅動、異步編程的特色剛纔已經詳細說過了,這裏再也不重複。
Node.js的性能不錯。按照創始人Ryan Dahl的說法,性能是Node.js考慮的重要因素,選擇C++和V8而不是Ruby或者其餘的虛擬機也是基於性能的目的。Node.js在設計上也是比較大膽,它以單進程、單線程模式運行(很吃驚,對吧?這和Javascript的運行方式一致),事件驅動機制是Node.js經過內部單線程高效率地維護事件循環隊列來實現的,沒有多線程的資源佔用和上下文切換,這意味着面對大規模的http請求,Node.js憑藉事件驅動搞定一切,習慣了傳統語言的網絡服務開發人員可能對多線程併發和協做很是熟悉,可是面對Node.js,咱們須要接受和理解它的特色。由此咱們是否能夠推測出這樣的設計會致使負載的壓力集中在CPU(事件循環處理?)而不是內存(還記得Java虛擬機拋出OutOfMemory異常的日子嗎?),眼見爲實,不如來看看淘寶共享數據平臺團隊對Node.js的性能測試:後端

  • 物理機配置:RHEL 5.二、CPU 2.2GHz、內存4G

  • Node.js應用場景:MemCache代理,每次取100字節數據

  • 鏈接池大小:50

  • 併發用戶數:100

  • 測試結果(socket模式):內存(30M)、QPS(16700)、CPU(95%)

從上面的結果,咱們能夠看到在這樣的測試場景下,qps可以達到16700次,內存僅佔用30M(其中V8堆佔用22M),CPU則達到95%,可能成爲瓶頸。此外,還有很多實踐者對Node.js作了性能分析,總的來講,它的性能讓人信服,也是受歡迎的重要緣由。既然Node.js採用單進程、單線程模式,那麼在現在多核硬件流行的環境中,單核性能出色的Node.js如何利用多核CPU呢?創始人Ryan Dahl建議,運行多個Node.js進程,利用某些通訊機制來協調各項任務。目前,已經有很多第三方的Node.js多進程支持模塊發佈,專欄後面的文章會詳細講述Node.js在多核CPU下的編程。

Node.js的另外一個特色是它支持的編程語言是Javascript。關於動態語言和靜態語言的優缺點比較在這裏再也不展開討論。只說三點:

var hostRequest = http.request(requestOptions,function(response) {
    var responseHTML ='';
    response.on('data', function (chunk) {
        responseHTML = responseHTML + chunk;
    });
    response.on('end',function(){
        console.log(responseHTML);
        // do something useful
   });
});

在上面的代碼中,咱們須要在end事件中處理responseHTML變量,因爲Javascript的閉包特性,咱們能夠在兩個回調函數以外定義responseHTML變量,而後在data事件對應的回調函數中不斷修改其值,並最終在end事件中訪問處理。

  • Javascript做爲前端工程師的主力語言,在技術社區中有至關的號召力。並且,隨着Web技術的不斷髮展,特別是前端的重要性增長,很多前端工程師開始試水」後臺應用「,在許多采用Node.js的企業中,工程師都表示由於習慣了Javascript,因此選擇Node.js。

  • Javascript的匿名函數和閉包特性很是適合事件驅動、異步編程,從helloworld例子中咱們能夠看到回調函數採用了匿名函數的形式來實現,很方便。閉包的做用則更大,看下面的代碼示例:

  • Javascript在動態語言中性能較好,有開發人員對Javacript、Python、Ruby等動態語言作了性能分析,發現Javascript的性能要好於其餘語言,再加上V8引擎也是同類的佼佼者,因此Node.js的性能也受益其中。

Node.js發展簡史

2009年2月,Ryan Dahl在博客上宣佈準備基於V8建立一個輕量級的Web服務器並提供一套庫。
2009年5月,Ryan Dahl在GitHub上發佈了最第一版本的部分Node.js包,隨後幾個月裏,有人開始使用Node.js開發應用。
2009年11月和2010年4月,兩屆JSConf大會都安排了Node.js的講座。
2010年年末,Node.js得到雲計算服務商Joyent資助,創始人Ryan Dahl加入Joyent全職負責Node.js的發展。
2011年7月,Node.js在微軟的支持下發布Windows版本。

Node.js應用案例

雖然Node.js誕生剛剛兩年多,可是其發展勢頭逐漸趕超Ruby/Rails,咱們在這裏列舉了部分企業應用Node.js的案例,聽聽來自客戶的聲音。
在社交網站LinkedIn最新發布的移動應用中,NodeJS是該移動應用的後臺基礎。LinkedIn移動開發主管Kiran Prasad對媒體表示,其整個移動軟件平臺都由NodeJS構建而成:

LinkedIn內部使用了大量的技術,可是在移動服務器這一塊,咱們徹底基於Node。
(使用它的緣由)第一,是由於其靈活性。第二,若是你瞭解Node,就會發現它最擅長的事情是與其餘服務通訊。移動應用必須與咱們的平臺API和數據庫交互。咱們沒有作太多數據分析。相比以前採用的Ruby on Rails技術,開發團隊發現Node在性能方面提升不少。他們在每臺物理機上跑了15個虛擬服務器(15個實例),其中4個實例便可處理雙倍流量。容量評估基於負載測試的結果。

企業社會化服務網站Yammer則利用Node建立了針對其自身平臺的跨域代理服務器,第三方的開發人員能夠經過該服務器實現從自身域託管的Javascript代碼與Yammer平臺API的AJAX通訊。Yammer平臺技術主管Jim Patterson對Node的優勢和缺點提出了本身的見解:

(優勢)由於Node是基於事件驅動和無阻塞的,因此很是適合處理併發請求,所以構建在Node上的代理服務器相比其餘技術實現(如Ruby)的服務器表現要好得多。此外,與Node代理服務器交互的客戶端代碼是由javascript語言編寫的,所以客戶端和服務器端都用同一種語言編寫,這是很是美妙的事情。
(缺點)Node是一個相對新的開源項目,因此不太穩定,它老是一直在變,並且缺乏足夠多的第三方庫支持。看起來,就像是Ruby/Rails當年的樣子。

知名項目託管網站GitHub也嘗試了Node應用。該Node應用稱爲NodeLoad,是一個存檔下載服務器(每當你下載某個存儲分支的tarball或者zip文件時就會用到它)。GitHub以前的存檔下載服務器採用Ruby編寫。在舊系統中,下載存檔的請求會建立一個Resque任務。該任務實際上在存檔服務器上運行一個git archive命令,從某個文件服務器中取出數據。而後,初始的請求分配給你一個小型Ruby Sinatra應用等待該任務。它其實只是在檢查memcache flag是否存在,而後再重定向到最終的下載地址上。舊系統運行大約3個Sinatra實例和3個Resque worker。GitHub的開發人員以爲這是Node應用的好機會。Node基於事件驅動,相比Ruby的阻塞模型,Node可以更好地處理git存檔。在編寫新下載服務器過程當中,開發人員以爲Node很是適合該功能,此外,他們還裏利用了Node庫socket.io來監控下載狀態。
不只在國外,Node的優勢也一樣吸引了國內開發人員的注意,淘寶就實際應用了Node技術:

MyFOX 是一個數據處理中間件,負責從一個MySQL集羣中提取數據、計算並輸出統計結果。用戶提交一段SQL語句,MyFOX根據該SQL命令的語義,生成各個數據庫分片所須要執行的查詢語句,併發送至各個分片,再將結果進行彙總和計算。 MyFOX的特色是CPU密集,無文件IO,並只處理只讀數據。起初MyFOX使用PHP編寫,但遇到許多問題。例如PHP是單線程的,MySQL又須要阻塞查詢,所以很難併發請求數據,後來的解決方案是使用nginx和dirzzle,並基於HTTP協議實現接口,並經過curl_multi_get命 令進行請求。不過MyFOX項目組最終仍是決定使用Node.js來實現MyFOX。

選擇Node.js有許多方面的緣由,好比考慮了興趣及社區發展,同時也但願能夠提升併發能力,榨乾CPU。例如,頻繁地打開和關閉鏈接會讓大量端口處於等待狀態,當併發數量上去以後,時常會由於端口不夠用(處於TIME_WAIT狀態)而致使鏈接失敗。以前每每是經過修改系統設置來減小等待時間以繞開這個錯誤,然而使用鏈接池即可以很好地解決這個問題。此外,之前MyFOX會在某些緩存失效的狀況下出現十分密集的訪問壓力,使用 Node.js即可以共享查詢狀態,讓某些請求「等待片刻」,以便系統從新填充緩存內容。

小結

本文簡要介紹了Node.js的基本知識,包括概念、特色、歷史、案例等等。做爲一個僅僅2歲的平臺,Node.js的發展勢頭有目共睹,愈來愈多的企業開始關注並嘗試Node.js,先後端開發人員應該瞭解相關的內容。

相關文章
相關標籤/搜索