Node.js是什麼?

(非原創,轉多方文章整合)javascript

簡介css

若是您據說過 Node,或者閱讀過一些文章,宣稱 Node 是多麼多麼的棒,那麼您可能會想:「Node 到底是什麼東西?」儘管不是針對全部人的,但 Node 多是某些人的正確選擇。前端

爲試圖解釋什麼是 Node.js,本文探究了它能解決的問題,它如何工做,如何運行一個簡單應用程序,最後,Node 什麼時候是和什麼時候不是一個好的解決方案。本文不涉及如何編寫一個複雜的 Node 應用程序,也不是一份全面的 Node 教程。閱讀本文應該有助於您決定是否應該學習 Node,以便將其用於您的業務。java

Node 旨在解決什麼問題?node

Node 公開宣稱的目標是 「旨在提供一種簡單的構建可伸縮網絡程序的方法」。當前的服務器程序有什麼問題?咱們來作個數學題。在 Java™ 和 PHP 這類語言中,每一個鏈接都會生成一個新線程,每一個新線程可能須要 2 MB 配套內存。在一個擁有 8 GB RAM 的系統上,理論上最大的併發鏈接數量是 4,000 個用戶。隨着您的客戶端基礎的增加,您但願您的 web 應用程序支持更多用戶,這樣,您必須添加更多服務器。固然,這會增長業務成本,尤爲是服務器成本、運輸成本和人工成本。除這些成本上升外,還有一個技術問題:用戶可能針對每一個請求使用不一樣的服務器,所以,任何共享資源都必須在全部服務器之間共享。例如,在 Java 中,靜態變量和緩存須要在每一個服務器上的 JVMs 之間共享。這就是整個 web 應用程序架構中的瓶頸:一個服務器可以處理的併發鏈接的最大數量。程序員

Node 解決這個問題的方法是:更改鏈接鏈接到服務器的方式。每一個鏈接都建立一個進程,該進程不須要配套內存塊,而不是爲每一個鏈接生成一個新的 OS 線程(並向其分配一些配套內存)。Node 聲稱它毫不會死鎖,由於它根本不容許使用鎖,它不會直接阻塞 I/O 調用。Node 還宣稱,運行它的服務器能支持數萬個併發鏈接。事實上,Node 經過將整個系統中的瓶頸從最大鏈接數量更改到單個系統的流量來改變服務器面貌。web

如今您有了一個能處理數萬條併發鏈接的程序,那麼您能經過 Node 實際構建什麼呢?若是您有一個 web 應用程序須要處理這麼多鏈接,那將是一件很 「恐怖」 的事!那是一種 「若是您有這個問題,那麼它根本不是問題」 的問題。在回答上面的問題以前,咱們先看看 Node 如何工做以及它被設計的如何運行。數據庫

Node 確定不是什麼編程

沒錯,Node 是一個服務器程序。可是,它確定不 像 Apache 或 Tomcat。那些服務器是獨立服務器產品,能夠當即安裝並部署應用程序。經過這些產品,您能夠在一分鐘內啓動並運行一個服務器。Node 確定不是這種產品。Apache 能添加一個 PHP 模塊來容許開發人員建立動態 web 頁,使用 Tomcat 的程序員能部署 JSPs 來建立動態 web 頁。Node 確定不是這種類型。ubuntu

在 Node 的早期階段(當前是 version 0.4.6),它還不是一個 「運行就緒」 的服務器程序,您還不能安裝它,向其中放置文件,擁有一個功能齊全的 web 服務器。即便是要實現 web 服務器在安裝完成後啓動並運行這個基本功能,也還須要作大量工做。

Node 如何工做

Node 自己運行 V8 JavaScript。等等,服務器上的 JavaScript?沒錯,您沒有看錯。服務器端 JavaScript 是一個相對較新的概念,這個概念是大約兩年前在 developerWorks 上討論 Aptana Jaxer 產品時提到的(參見 參考資料)。儘管 Jaxer 一直沒有真正流行,但這個理念自己並非高不可攀的 — 爲什麼不能在服務器上使用客戶機上使用的編程語言?

什麼使 V8?V8 JavaScript 引擎是 Google 用於他們的 Chrome 瀏覽器的底層 JavaScript 引擎。不多有人考慮 JavaScript 在客戶機上實際作了些什麼?實際上,JavaScript 引擎負責解釋並執行代碼。使用 V8,Google 建立了一個以 C++ 編寫的超快解釋器,該解釋器擁有另外一個獨特特徵;您能夠下載該引擎並將其嵌入任何 應用程序。它不只限於在一個瀏覽器中運行。所以,Node 實際上使用 Google 編寫的 V8 JavaScript 引擎並將其重建爲在服務器上使用。太完美了!既然已經有一個不錯的解決方案可用,爲什麼還要建立一種新語言呢?

事件驅動編程

許多程序員接受的教育使他們認爲,面向對象編程是完美的編程設計,而對其餘編程方法不屑一顧。Node 使用一個所謂的事件驅動編程模型。

清單 1. 客戶端上使用 jQuery 的事件驅動編程

// jQuery code on the client-side showing how Event-Driven programming works
 
// When a button is pressed, an Event occurs - deal with it
// directly right here in an anonymous function, where all the
// necessary variables are present and can be referenced directly
$("#myButton").click(function(){
     if ($("#myTextField").val() != $(this).val())
         alert("Field must match button text");
});

 

<span class="Apple-style-span" style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;">實際上,服務器端和客戶端沒有任何區別。沒錯,這沒有按鈕點擊操做,也沒有向文本字段鍵入的操做,但在一個更高的層面上,事件正在 發生。一個鏈接被創建 — 事件!數據經過鏈接接收 — 事件!數據經過鏈接中止 — 事件!</span>

爲何這種設置類型對 Node 很理想?JavaScript 是一種很棒的事件驅動編程語言,由於它容許匿名函數和閉包,更重要的是,任何寫過代碼的人都熟悉它的語法。事件發生時調用的回調函數能夠在捕獲事件處編寫。這樣,代碼容易編寫和維護,沒有複雜的面向對象框架,沒有接口,沒有在上面架構任何內容的潛能。只需監聽事件,編寫一個回調函數,而後,事件驅動編程將照管好一切!

示例 Node 應用程序

最後,咱們來看一些代碼!讓咱們將討論過的全部內容綜合起來,建立咱們的第一個 Node 應用程序。因爲咱們已經知道,Node 對於處理高流量應用程序很理想,咱們就來建立一個很是簡單的 web 應用程序 — 一個爲實現最大速度而構建的應用程序。下面是 「老闆」 交代的關於咱們的樣例應用程序的具體要求:建立一個隨機數字生成器 RESTful API。這個應用程序應該接受一個輸入:一個名爲 「number」 的參數。而後,應用程序返回一個介於 0 和該參數之間的隨機數字,並將生成的數字返回調用者。因爲 「老闆」 但願它成爲一個普遍流行的應用程序,所以它應該能處理 50,000 個併發用戶。咱們來看看代碼:

清單 2. Node 隨機數字生成器

// these modules need to be imported in order to use them.
// Node has several modules.  They are like any #include
// or import statement in other languages
var http = require("http");
var url = require("url");
 
// The most important line in any Node file.  This function
// does the actual process of creating the server.  Technically,
// Node tells the underlying operating system that whenever a
// connection is made, this particular callback function should be
// executed.  Since we're creating a web service with REST API,
// we want an HTTP server, which requires the http variable
// we created in the lines above.
// Finally, you can see that the callback method receives a 'request'
// and 'response' object automatically.  This should be familiar
// to any PHP or Java programmer.
http.createServer(function(request, response) {
 
     // The response needs to handle all the headers, and the return codes
     // These types of things are handled automatically in server programs
     // like Apache and Tomcat, but Node requires everything to be done yourself
     response.writeHead(200, {"Content-Type": "text/plain"});
 
     // Here is some unique-looking code.  This is how Node retrives
     // parameters passed in from client requests.  The url module
     // handles all these functions.  The parse function
     // deconstructs the URL, and places the query key-values in the
     // query object.  We can find the value for the "number" key
     // by referencing it directly - the beauty of JavaScript.
     var params = url.parse(request.url, true).query;
     var input = params.number;
 
     // These are the generic JavaScript methods that will create
     // our random number that gets passed back to the caller
     var numInput = new Number(input);
     var numOutput = new Number(Math.random() * numInput).toFixed(0);
 
     // Write the random number to response
     response.write(numOutput);
 
     // Node requires us to explicitly end this connection.  This is because
     // Node allows you to keep a connection open and pass data back and forth,
     // though that advanced topic isn't discussed in this article.
     response.end();
 
   // When we create the server, we have to explicitly connect the HTTP server to
   // a port.  Standard HTTP port is 80, so we'll connect it to that one.
}).listen(80);
 
// Output a String to the console once the server starts up, letting us know everything
// starts up correctly
console.log("Random Number Generator Running...");

 

將上面的代碼放到一個名爲 「random.js」 的文件中。如今,要啓動這個應用程序並運行它(進而建立 HTTP 服務器並監聽端口 80 上的鏈接),只需在您的命令提示中輸入如下命令:% node random.js。下面是服務器已經啓動並運行時它看起來的樣子:

root@ubuntu:/home/moila/ws/mike# node random.js
Random Number Generator Running...

 

訪問應用程序

應用程序已經啓動並運行。Node 正在監放任何鏈接,咱們來測試一下。因爲咱們建立了一個簡單的 RESTful API,咱們可使用咱們的 web 瀏覽器來訪問這個應用程序。鍵入如下地址(確保您完成了上面的步驟):http://localhost/?number=27。

您的瀏覽器窗口將更改到一個介於 0 到 27 之間的隨機數字。單擊瀏覽器上的 「從新載入」 按鈕,將獲得另外一個隨機數字。就是這樣,這就是您的第一個 Node 應用程序!

Node 對什麼有好處?

到此爲止,應該可以回答 「Node 是什麼」 這個問題了,但您可能還不清楚何時應該使用它。這是一個須要提出的重要問題,由於 Node 對有一些東西有好處,但相反,對另外一些東西而言,目前 Node 可能不是一個好的解決方案。您須要當心決定什麼時候使用 Node,由於在錯誤的狀況下使用它可能會致使一個多餘編碼的 LOT。

它對什麼有好處?

正如您此前所看到的,Node 很是適合如下狀況:您預計可能有很高的流量,而在響應客戶端以前服務器端邏輯和處理所需不必定是巨大的。Node 表現出衆的典型示例包括:

RESTful API

提供 RESTful API 的 web 服務接收幾個參數,解析它們,組合一個響應,並返回一個響應(一般是較少的文本)給用戶。這是適合 Node 的理想狀況,由於您能夠構建它來處理數萬條鏈接。它還不須要大量邏輯;它只是從一個數據庫查找一些值並組合一個響應。因爲響應是少許文本,入站請求時少許文本,所以流量不高,一臺機器甚至也能夠處理最繁忙的公司的 API 需求。

Twitter 隊列

想像一下像 Twitter 這樣的公司,它必須接收 tweets 並將其寫入一個數據庫。實際上,每秒幾乎有數千條 tweets 達到,數據庫不可能及時處理高峯時段須要的寫入數量。Node 成爲這個問題的解決方案的重要一環。如您所見,Node 能處理數萬條入站 tweets。它能迅速輕鬆地將它們寫入一個內存排隊機制(例如 memcached),另外一個單獨進程能夠從那裏將它們寫入數據庫。Node 在這裏的角色是迅速收集 tweet 並將這個信息傳遞給另外一個負責寫入的進程。想象一下另外一種設計 — 一個常規 PHP 服務器本身試圖處理對數據庫的寫入 — 每一個 tweet 將在寫入數據庫時致使一個短暫的延遲,這是由於數據庫調用正在阻塞通道。因爲數據庫延遲,一臺這樣設計的機器每秒可能只能處理 2000 條入站 tweets。每秒 100 萬條 tweets 須要 500 個服務器。相反,Node 能處理每一個鏈接而不會阻塞通道,從而能捕獲儘量多的 tweets。一個能處理 50,000 條 tweets 的 Node 機器只須要 20 個服務器。

映像文件服務器

一個擁有大型分佈式網站的公司(好比 Facebook 或 Flickr)可能會決定將全部機器只用於服務映像。Node 將是這個問題的一個不錯的解決方案,由於該公司能使用它編寫一個簡單的文件檢索器,而後處理數萬條鏈接。Node 將查找映像文件,返回文件或一個 404 錯誤,而後什麼也不用作。這種設置將容許這類分佈式網站減小它們服務映像、.js 和 .css 文件等靜態文件所需的服務器數量。

它對什麼有壞處?

固然,在某些狀況下,Node 並不是理想選擇。下面是 Node 不擅長的領域:

動態建立的頁

目前,Node 沒有提供一種默認方法來建立動態頁。例如,使用 JavaServer Pages (JSP) 技術時,能夠建立一個在這樣的 JSP 代碼段中包含循環的 index.jsp 頁。Node 不支持這類動態的、HTML 驅動的頁面。一樣,Node 不太適合做爲 Apache 和 Tomcat 這樣的網頁服務器。所以,若是您想在 Node 中提供這樣一個服務器端解決方案,必須本身編寫整個解決方案。PHP 程序員不想在每次部署 web 應用程序時都編寫一個針對 Apache 的 PHP 轉換器,當目前爲止,這正是 Node 要求您作的。

關係數據庫重型應用程序

Node 的目的是快速、異步和非阻塞。數據庫並不必定分享這些目標。它們是同步和阻塞的,由於讀寫時對數據庫的調用在結果生成以前將一直阻塞通道。所以,一個每一個請求都須要大量數據庫調用、大量讀取、大量寫入的 web 應用程序很是不適合 Node,這是由於關係數據庫自己就能抵銷 Node 的衆多優點。(新的 NoSQL 數據庫更適合 Node,不過那徹底是另外一個主題了。)

結束語

問題是 「什麼是 Node.js?」 應該已經獲得解答。閱讀本文以後,您應該能經過幾個清晰簡潔的句子回答這個問題。若是這樣,那麼您已經走到了許多編碼員和程序員的前面。我和許多人都談論過 Node,但它們對 Node 到底是什麼一直很迷惑。能夠理解,他們具備的是 Apache 的思惟方式 — 服務器是一個應用程序,將 HTML 文件放入其中,一切就會正常運轉。而 Node 是目的驅動的。它是一個軟件程序,使用 JavaScript 來容許程序員輕鬆快速地建立快速、可伸縮的 web 服務器。Apache 是運行就緒的,而 Node 是編碼就緒的。

Node 完成了它提供高度可伸縮服務器的目標。它並不分配一個 「每一個鏈接一個線程」 模型,而是使用一個 「每一個鏈接一個流程」 模型,只建立每一個鏈接須要的內存。它使用 Google 的一個很是快速的 JavaScript 引擎:V8 引擎。它使用一個事件驅動設計來保持代碼最小且易於閱讀。全部這些因素促成了 Node 的理想目標 — 編寫一個高度可伸縮的解決方案變得比較容易。

與理解 Node 是 什麼一樣重要的是,理解它不是 什麼。Node 並非 Apache 的一個替代品,後者旨在使 PHP web 應用程序更容易伸縮。事實確實如此。在 Node 的這個初始階段,大量程序員使用它的可能性不大,但在它能發揮做用的場景中,它的表現很是好。

 

Node.js is the New Black

    若是你去年注意過技術方面的新聞,我敢說你至少看到node.js不下一兩次。那麼問題來了「node.js是什麼?」。有些人沒準會告訴你「這是一種經過JavaScript語言開發web服務端的東西」。若是這種晦澀解釋還沒把你搞暈,你沒準會接着問:「爲何咱們要用node.js?」,別人通常會告訴你:node.js有非阻塞,事件驅動I/O等特性,從而讓高併發(high concurrency)在的輪詢(Polling)和comet構建的應用中成爲可能。

    當你看完這些解釋以爲跟看天書同樣的時候,你估計也懶得繼續問了。不過沒事。我這篇文章就是在避開高端術語的同時,幫助你你理解node.js的。

    瀏覽器給網站發請求的過程一直沒怎麼變過。當瀏覽器給網站發了請求。服務器收到了請求,而後開始搜尋被請求的資源。若是有須要,服務器還會查詢一下數據庫,最後把響應結果傳回瀏覽器。不過,在傳統的web服務器中(好比Apache),每個請求都會讓服務器建立一個新的進程來處理這個請求。

    後來有了Ajax。有了Ajax,咱們就不用每次都請求一個完整的新頁面了,取而代之的是,每次只請求須要的部分頁面信息就能夠了。這顯然是一個進步。可是好比你要建一個FriendFeed這樣的社交網站(相似人人網那樣的刷朋友新鮮事的網站),你的好友會隨時的推送新的狀態,而後你的新鮮事會實時自動刷新。要達成這個需求,咱們須要讓用戶一直與服務器保持一個有效鏈接。目前最簡單的實現方法,就是讓用戶和服務器之間保持長輪詢(long polling)。

    HTTP請求不是持續的鏈接,你請求一次,服務器響應一次,而後就完了。長輪訓是一種利用HTTP模擬持續鏈接的技巧。具體來講,只要頁面載入了,無論你需不須要服務器給你響應信息,你都會給服務器發一個Ajax請求。這個請求不一樣於通常的Ajax請求,服務器不會直接給你返回信息,而是它要等着,直到服務器以爲該給你發信息了,它纔會響應。好比,你的好友發了一條新鮮事,服務器就會把這個新鮮事當作響應發給你的瀏覽器,而後你的瀏覽器就刷新頁面了。瀏覽器收到響應刷新完以後,再發送一條新的請求給服務器,這個請求依然不會當即被響應。因而就開始重複以上步驟。利用這個方法,可讓瀏覽器始終保持等待響應的狀態。雖然以上過程依然只有非持續的Http參與,可是咱們模擬出了一個看似持續的鏈接狀態

    咱們再看傳統的服務器(好比Apache)。每次一個新用戶連到你的網站上,你的服務器就得開一個鏈接。每一個鏈接都須要佔一個進程,這些進程大部分時間都是閒着的(好比等着你好友發新鮮事,等好友發完纔給用戶響應信息。或者等着數據庫返回查詢結果什麼的)。雖然這些進程閒着,可是照樣佔用內存。這意味着,若是用戶鏈接數的增加到必定規模,你服務器沒準就要耗光內存直接癱了。

    這種狀況怎麼解決?解決方法就是剛纔上邊說的:非阻塞事件驅動。這些概念在咱們談的這個情景裏面其實沒那麼難理解。你把非阻塞的服務器想象成一個loop循環,這個loop會一直跑下去。一個新請求來了,這個loop就接了這個請求,把這個請求傳給其餘的進程(好比傳給一個搞數據庫查詢的進程),而後響應一個回調(callback)。完事了這loop就接着跑,接其餘的請求。這樣下來。服務器就不會像以前那樣傻等着數據庫返回結果了。

    若是數據庫把結果返回來了,loop就把結果傳回用戶的瀏覽器,接着繼續跑。在這種方式下,你的服務器的進程就不會閒着等着。從而在理論上說,同一時刻的數據庫查詢數量,以及用戶的請求數量就沒有限制了。服務器只在用戶那邊有事件發生的時候才響應,這就是事件驅動。

    FriendFeed是用基於Python的非阻塞框架Tornado (知乎也用了這個框架) 來實現上面說的新鮮事功能的。不過,Node.js就比前者更妙了。Node.js的應用是經過javascript開發的,而後直接在Google的變態V8引擎上跑。用了Node.js,你就不用擔憂用戶端的請求會在服務器裏跑了一段可以形成阻塞的代碼了。由於javascript自己就是事件驅動的腳本語言。你回想一下,在給前端寫javascript的時候,更多時候你都是在搞事件處理和回調函數。javascript自己就是給事件處理量身定製的語言。

    Node.js仍是處於初期階段。若是你想開發一個基於Node.js的應用,你應該會須要寫一些很底層代碼。可是下一代瀏覽器很快就要採用WebSocket技術了,從而長輪詢也會消失。在Web開發裏,Node.js這種類型的技術只會變得愈來愈重要。

相關文章
相關標籤/搜索