有關Node.js的技術報道愈來愈多,Node.js的寫法也是五花八門,有寫成 NodeJS的,有寫成Nodejs的,到底哪種寫法最標準呢,咱們不妨遵循官方的說法。在Node.js的官方網站上,一直將其項目稱之爲」Node「或者」Node.js「
, 沒有發現其餘的說法,」Node「用的最多,考慮到Node這個單詞的意思和用途太普遍,容易讓開發人員誤解,咱們採用了第二種稱呼——」Node.js「,js的後綴點出了Node項目的本意
,其餘的名稱五花八門,沒有確切的出處,咱們不推薦使用。javascript
Node公開宣稱的目標是 「旨在提供一種簡單的構建可伸縮網絡程序的方法」
。當前的服務器程序有什麼問題?咱們來作個數學題。在 Java™ 和 PHP 這類語言中,每一個鏈接都會生成一個新線程,每一個新線程可能須要 2 MB 的配套內存。在一個擁有 8 GB RAM 的系統上,理論上最大的併發鏈接數量是 4,000 個用戶。隨着您的客戶羣的增加,若是但願您的 Web 應用程序支持更多用戶,那麼,您必須添加更多服務器。固然,這會增長服務器成本、流量成本和人工成本等成本。除這些成本上升外,還有一個潛在技術問題,即用戶可能針對每一個請求使用不一樣的服務器,所以,任何共享資源都必須在全部服務器之間共享。 鑑於上述全部緣由,整個 Web 應用程序架構(包括流量、處理器速度和內存速度)中的瓶頸是:服務器可以處理的併發鏈接的最大數量
。前端
Node 解決這個問題的方法是:更改鏈接到服務器的方式
。每一個鏈接發射一個在 Node 引擎的進程中運行的事件,而不是爲每一個鏈接生成一個新的 OS 線程(併爲其分配一些配套內存)。Node 聲稱它毫不會死鎖,由於它根本不容許使用鎖,它不會直接阻塞 I/O 調用
。Node 還宣稱,運行它的服務器能支持數萬個併發鏈接。java
看到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運行環境(支持的系統包括Linux、Windows)
,這意味着你能夠 編寫系統級或者服務器端的Javascript代碼,交給Node.js來解釋執行
,簡單的命令相似於:node
#node helloworld.js
複製代碼
Node.js採用了Google Chrome瀏覽器的V8引擎,性能很好, 同時還提供了不少系統級的API
,如文件操做、網絡編程等。瀏覽器端的Javascript代碼在運行時會受到各類安全性的限制,對客戶系統的操做有限
。相比之下, Node.js則是一個全面的後臺運行時,爲Javascript提供了其餘語言可以實現的許多功能
。web
事件驅動這個詞並不陌生,在某些傳統語言的網絡編程中,咱們會用到回調函數,好比當socket資源達到某種狀態時,註冊的回調函數就會執行。Node.js的設計思想中以事件驅動爲核心,它提供的絕大多數API都是基於事件的、異步的風格
。以Net模塊爲例,其中的net.Socket對象就有如下事件:connect、data、 end、timeout、drain、error、close等,使用Node.js的開發人員須要根據本身的業務邏輯註冊相應的回調函數。這些回調函數都是異步執行的
,這意味着雖然在代碼結構中,這些函數看似是依次註冊的,可是 它們並不依賴於自身出現的順序,而是等待相應的事件觸發
。事件驅動、異步編程的設計,重要的優點在於,充分利用了系統資源,執行代碼無須阻塞等待某種操做完成,有限的資源能夠用於其餘的任務
。此類設計很是適合於 後端的網絡服務編程,Node.js的目標也在於此
。在服務器開發中,併發的請求處理是個大問題,阻塞式的函數會致使資源浪費和時間延遲。經過事件註冊、異步函數,開發人員能夠提升資源的利用率,性能也會改善。編程
從Node.js提供的支持模塊中,咱們能夠 看到包括文件操做在內的許多函數都是異步執行的
,這和傳統語言存在區別,並且爲了方便服務器開發,Node.js的網絡模塊特別多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等,開發人員能夠在此基礎上快速構建Web服務器。以簡單的helloworld.js爲例:後端
// 全局方法require()是用來導入模塊的,通常直接把require()方法的返回值賦值給一個變量,在JavaScript代碼中直接使用此變量便可。require("http")就是加載系統預置的http模塊。
var http = require('http');
// http.createServer是模塊的方法,目的就是建立並返回一個新的web server對象,而且給服務綁定一個回調,用以處理請求。
http.createServer(function (req, res) {
// 使用response.writeHead()函數發送一個HTTP狀態200和HTTP頭的內容類型(content-type)
// 使用response.write()函數在HTTP相應主體中發送文本「Hello World"
res.writeHead(200, {'Content-Type': 'text/plain'});
// 完成響應
res.end('Hello World\n');
// 經過http.listen()方法就可讓該HTTP服務器在特定端口監聽。
}).listen(80, "127.0.0.1");
// console.log就不用多說了,瞭解firebug的都應該知道,Node實現了這個方法。
console.log('Server running at http://127.0.0.1:80/');
複製代碼
上面的代碼搭建了一個簡單的http服務器(運行示例部署 在http://127.0.0.1中能夠訪問),在本地監聽80端口,對於任意的http請求,服務器都返回一個頭部狀態碼爲200、Content-Type值爲'text/plain'的"Hello World"文字響應。從這個小例子中,咱們能夠看出幾點:瀏覽器
當咱們使用 http.createServer 方法的時候,咱們固然不僅是想要一個偵聽某個端口的服務器,咱們還想要它在服務器收到一個HTTP請求的時候作點什麼。問題是,這是異步的:請求任什麼時候候均可能到達,可是咱們的服務器卻跑在一個單進程中
。咱們建立了服務器,而且向建立它的方法傳遞了一個函數。不管什麼時候咱們的服務器收到一個請求,這個函數就會被調用
。安全
爲何這種事件驅動對 Node 很理想?JavaScript 是一種很棒的事件驅動編程語言
,由於它容許使用匿名函數和閉包,更重要的是,任何寫過代碼的人都熟悉它的語法。事件發生時調用的回調函數能夠在捕獲事件處進行編寫。這樣可使代碼容易編寫和維護,沒有複雜的面向對象框架,沒有接口,沒有過分設計的可能性。只需監聽事件,編寫一個回調函數,其餘事情均可以交給系統處理!服務器
下面咱們來講說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的性能測試:
從上面的結果,咱們能夠看到在這樣的測試場景下,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事件中訪問處理。
同時也但願能夠提升併發能力,榨乾CPU
。