Node.js具體解析

介紹javascript

JavaScript 高漲的人氣帶來了很是多變化。以致於如今使用其進行網絡開發的形式也變得大相徑庭了。就如同在瀏覽器中同樣,如今咱們也可以在server上執行 JavaScript ,從前端跨越到後端,這樣巨大的反差讓人不可思議。因爲只在幾年前 Javascript 還如同 Flash 或者 Java applet 那樣嵌入網頁在沙箱環境中執行。html

在深刻Node.js以前。你可能需要閱讀和了解使用跨棧式JavaScript(JavaScript across the stack)帶來的優勢,它統一了編程語言和數據格式(JSON),讓你能最佳地重用開發者資源。由於這不少其它的是關於 JavaScript 的特色。這裏就只是多討論它。前端

但它確實是一個讓人在開發環節中使用 Node 的關鍵的長處。java

正如維基百科 所說:「Node.js 是谷歌 V8 引擎、libuv平臺抽象層 以及主體使用 Javscript 編寫的核心庫三者集合的一個包裝外殼。」 除此以外,值得注意的是,Node.js 的做者瑞恩·達爾 (Ryan Dahl) 的目標是建立具備實時推送能力的站點。在 Node.js 中,他給了開發人員一個使用事件驅動來實現異步開發的優秀解決方式。node

(注:V8是谷歌開發的,眼下公認最快的 Javascript 解析引擎,libuv 是一個開源的、爲 Node 定製而生的跨平臺的異步 IO 庫。)nginx

簡而言之:Node.js 在實時的 Web應用上採用了基於 WebSocket 的推送技術。git

這意味着什麼樣的革命性?Well,在通過了20多年的基於無狀態的請求-返機制的無狀態交互以後,咱們最終有了實時的。雙向鏈接的web應用。client和server端均可以發起通訊。能夠自由地交換數據。與此造成鮮明對照的是傳統的 web響應模式,client老是主動發起通訊而服務端被動返回。github

此外,這些都是基於執行在標準80port上的開放Web組件(HTML、CSS和JS)。web

可能有人會說,咱們已經使用 Flash 和 Java Applet 的形式很是多年了——但實際上。這些方式僅僅是使用網絡將數據傳遞到client上的沙箱環境。他們都是隔離執行的。而且經常操做到需要額外的權限之類的非標準port。redis

憑藉其獨特的優點,Node.js的現在已經在不少著名公司的產品中起到了關鍵做用。

在這篇文章中。咱們不只將討論這些優點是怎樣實現的,而且也會討論爲何你使用 Node.js 來替代一些經典的Web應用程序模型。

Node.js 是怎樣工做的?

Node.js 的主要思路是:使用非堵塞的,事件驅動的 I/O 操做來保持在處理跨平臺 (across distributed devices) 數據密集型實時應用時的輕巧高效。這聽起來有點繞口。

它的真正含義是,Node.js 不是一個即將主導Web開發的世界的銀彈級的平臺。

相反,它是一個知足特別需求的平臺。你確定不會但願使用 Node.js 去作 CPU密集型操做。其實,使用它進行繁重的計算等於摒棄 Node 差點兒所有的長處。Node 真正的亮點在於建設高性能,高擴展性的互聯網應用——因爲它能夠處理龐大的並且高吞吐量的併發鏈接。

它的工做原理是至關有趣的。傳統的網絡服務技術,是每個新增一個鏈接(請求)便生成一個新的線程,這個新的線程會佔用系統內存,終於會佔掉所有的可用內存。

而 Node.js 只只執行在一個單線程中,使用非堵塞的異步 I/O 調用,所有鏈接都由該線程處理,在 libuv 的加分下,可以贊成其支持數萬併發鏈接(所有掛在該線程的事件循環中)。

toptal-blog-1_B

作一個簡單的計算: 若是是普通的Web程序,新接入一個鏈接會佔用 2M 的內存,在有 8GB RAM的系統上執行時, 算上線程之間上下文切換的成本,併發鏈接的最大理論值則爲 4000 個。這是在傳統 Web服務端技術下的處理狀況。

而 Node.js 則達到了約 1M 一個併發鏈接的拓展級別 (相關證實).

固然。在所有client的請求共享單一線程時也會有問題, 這也是一個編寫 Node.js 應用的潛在缺陷. 首先, 大量的計算可能會使得 Node 的單線程臨時失去反應, 並致使所有的其它client的請求一直堵塞, 直到計算結束才恢復正常。 其次,開發者需要很當心,不要讓一個 Exception 堵塞核心的事件循環,因爲這將致使 Node.js 實例的終止(實際上就是程序崩潰)。( 筆者注:如 PHP 中某個頁面掛掉是不會影響站點執行的,但是 Nodejs 是一個線程一個線程來處理所有的連接,因此不管是計算卡了或者是被異常堵塞了均可能會影響到其它所有的連接。解決方式在稍後討論。)

用來避免異常拋出時中斷進程的方法是將異常使用回調傳遞出去(而不是拋出他們。就像在其它環境中同樣)。即便一些未處理的異常堵塞了程序,依然有多種應對的解決方式,而且也有很是多可用於監視 Node 進程來運行必要的崩潰後恢復工做的策略和工具(儘管你將沒法恢復用戶的 Session )。最多見的是使用 Forever 模塊。或者採用其它的外部系統工具如 upstart and monit

NPM: The Node Package Manager

當咱們討論 Node.js 的時候,一個絕對不該該忽略地方就是默認內置的模塊管理工具 —— NPM。 其靈感來源與 Ruby Gems(具備版本號和依賴管理功能。可以經過在線資料庫便捷安裝可重用的組件的管理工具)。

一個完整的公用模塊列表可以在 NPM 的站點上找到(https:://npmjs.org/),或者經過使用與 Node.js 一同安裝的 NPM CLI 工具放問到。該模塊的生態系統向所有人開放,不論什麼人都可以公佈本身的模塊,所有的模塊都可以在 NPM 資料庫中找到。你可以在 http://howtonode.org/introduction-to-npm 頁面找到 NPM 的一個簡要介紹(有點舊,但依然能看)。

眼下很流行的一些 NPM 模塊有:

  • express – Express.js,是一個簡潔而靈活的 node.js Web應用框架, 並且已是現在大多數 Node.js 應用的標準框架。你已經可以在很是多 Node.js 的書籍中看到它了。
  • connect – Connect 是一個 Node.js 的 HTTP 服務拓展框架,提供一個高性能的「插件」集合,以中間件聞名,是 Express 的基礎部分之中的一個。

  • socket.io 和 sockjs – 眼下服務端最流行的兩個 websocket 組件。
  • Jade – 流行的模板引擎之中的一個,並且是 Express.js 的默認模板引擎。其靈感來源於 HAML。

  • mongo 和 mongojs – 封裝了 MongoDB 的的各類 API,只是筆者尋常工做用的是 mongoose 也很是推薦。
  • redis – Redis 的client函數庫.
  • coffee-script – CoffeeScript 編譯器,贊成開發人員使用 Coffee 來編寫他們的 Node.js 程序。
  • underscore (lodashlazy) – 最流行的 JavaScript 工具庫 , 用於 Node.js 的封裝包,以及兩個採取略有不一樣的實現方法來得到更好性能的同行。
  • forever – 多是用來確保 node 腳本持續執行的最流行的工具。

還有很是多好的模塊。這裏就不一一列舉了(但願沒有冒犯到沒列舉的)。

Node.js 應該用在什麼地方

聊天

聊天是最典型的多用戶實時交互的應用。從 IRC 開始,有不少開源或者不開源的協議都執行在非標準port上,而現在,使用 Node.js 則可以解決這些問題——在標準的80port執行 WebSockets。

聊天應用程序是最能體現 Node.js 長處的樣例:輕量級、高流量並且能良好的應對跨平臺設備上執行密集型數據(儘管計算能力低)。

同一時候。聊天也是一個很是值得學習的用例。因爲它很是easy,並且涵蓋了眼下爲止一個典型的 Node.js 會用到的大部分解決方式。

讓咱們試着來描繪它怎樣工做。

在最簡單的狀況下。咱們佈置了一個聊天室在咱們的站點上,用戶可以在上面發消息,固然是一對多的形式。

好比,若是總共同擁有三我的鏈接到咱們的站點上。

在服務端這邊。 咱們有一個使用 Express.js 搭建的簡單網站,該網站實現了兩件事 1) 處理路徑爲 ‘/’ 的GET請求時,下發包含一個留言板以及一個發送信息的 ‘發送’ button的頁面 2) 一個監聽client發送新消息的 websockets 服務。

在client這邊,咱們有一個 HTML 頁面,上面有個兩個 js 方法,一個是用於觸發事件的 「發送」 button,這會把把輸入的消息經過 webscoket 發送,還有一個方法是用 webscoket 在client上監聽服務端來的推送(好比。其它用戶發送的消息)。

當有一個client發送消息的時候,發生的事情是:

  1. 瀏覽器上,點擊發送button觸發了 js 函數。將輸入框中的文字經過 websocket 消息發送到server的 websocket client(頁面初始化載入的時候鏈接的)。
  2. 服務端的 websocket 組件收到 消息,而後經過廣播方法轉發到其它所有鏈接的client。

  3. 經過頁面上執行的 websocket client組件,所有的client都能收到這條推送的新消息。接着 js 處理函數可以把這個消息加入到文字框內。

toptal-blog-2_B

這是一個最簡單的樣例。假設要更好的解決方式。你可以使用 Redis 數據庫作一個簡單的緩存。在一個更高級的解決方式中,你可能需要一個消息路由來專門處理消息隊列。並且需要一個更強健的發送機制。比方發送的時候覆蓋上臨時離線的用戶或者爲離線的注冊用戶存儲還沒有接收的消息等等。但是不論你作了怎麼樣的改進,Node.js 都將遵循一個基本原則:響應事件,處理多個併發鏈接,並保持流動性的用戶體驗。

對象數據庫接口(API ON TOP OF AN OBJECT DB)

雖然,Node.js 確實很擅長實時交互的應用,同一時候它也十分適合經過對象數據庫(object DB)來查詢數據(如 MongoDB)。

以 JSON 格式存儲的數據贊成 Node.js 直接處理。不需要糾結數據轉換和匹配的問題。

舉個樣例。假設你正在使用 Rails。你會將 JSON 數據轉成 二進制的 model,當數據再被 Backbone.js, Angular.js 或者 jQuery AJAX 之類的調用又要轉回 JSON。假設是 Nodejs 的話,你可以經過一個 REST API 簡單的導出 JSON 對象以供client使用。

另外,從數據庫讀寫時候假設使用的是 MongoDB 的話。你也不用操心的 JSON 與不論什麼數據之間的格式問題。

總之。你可以避免多元的數據轉換問題,不管是在client、服務端仍是數據庫。

隊列輸入

假設你正在接收一個高量併發的數據,你的數據庫可能會成爲你處理的瓶頸。正如上面的描寫敘述。Node.js 可以輕鬆的處理併發鏈接。

但是,由於數據庫操做是一個堵塞的操做(在這樣的狀況下),這就是麻煩的地方。Node.js的解決方式是,在數據真正的寫入以前就認可client的數據是真實的。

用這樣的方法,在高負載的時候系統繼續維持它的響應。這在當client不需要嚴格確認一個數據是否成功的被寫入時特別實用。典型的樣例包含:日誌記錄或者用戶跟蹤數據(user-tracking data)的記錄,這會被分批處理並且在稍後才使用;同一時候也包含終於一致性(so, 常用於 NoSQL)可以接受,不需要立刻反應的操做(好比 Facebook 上更新點讚的數目)。

數據經過某些緩存或者消息隊列的基礎組件(好比 RabbitMQ, ZeroMQ)進入隊列。並且經過一個獨立的數據庫批量寫入進程來一一消化。或者經過一個更高性能的計算密集型後端服務來進行處理。其它的語言/框架也可以實現類似的操做。但在一樣的配置下是達不到 nodejs 的高吞吐量與高併發。

toptal-blog-3_B

簡單的說:使用 Node,你可以把數據庫操做扔到一邊並在稍後處理它們,若是他們成功了同樣繼續運行下去。

(筆者注:在開發中一般的狀況通常是,種耗時的操做經過回調函數來異步處理,主線程繼續往下運行)

數據流

在較爲傳統的網絡平臺上。HTTP 的請求和響應更像是孤立的事件;然而其實,他們都是數據流。這一觀察結果在 Nodejs 上可以用來創建一些很是酷的功能。因爲數據通以流的形式接收,而咱們可以在站點上在線處理正在上傳中的文件。這種話,就可以實現實時的音頻和視頻編碼,以及在不一樣數據源之間進行代碼(代理見下一段)。

(筆者注:Node 有取代如 apache 這種 webserver 處理數據。因此開發人員可以直接收到client一份一份上傳的數據,並實時處理。上面這段話聽起來有點抽象。只是各位可以簡單的想象一下不需要開 YY 或者 QQ。打開網頁就能進行語音視頻的功能。)

代理

Node.js 可以經過異步的方式處理大量的併發鏈接,因此很是easy做爲服務端的代理來使用。這在與不一樣響應時間的不一樣服務之間進行代理。或者是收集來自多個來源的數據時尤事實上用。

舉個樣例:考慮一個server端的應用程序和第三方資源進行通訊以更新自不一樣來源的數據,或者將服務端上的一些圖像和視頻資源存儲到第三方雲服務。

儘管專用代理server確實存在,但是假設你尚未專用的代理server,或者你需要一個本地開發的解決方式,那麼使用 Node 來作代理多是更好的選擇。關於這個解決方式。個人意思是指當你在開發的時候,你可以使用Node.js的開發環境搭建一個服務來處理對資源和代理的請求。而在生產環境下,你可以使用專用的代理服務(比方nginx。HAProxy等)來處理這些交互。

股票操盤手的儀表盤

讓咱們繼續討論應用程序這塊。

實時網絡的解決方式可以很是輕鬆的實現證券交易軟件——用於跟蹤股票的價格,運行計算、作技術分析,同一時候生成報表。

使用一個實時的的基於網頁的解決方式。將會贊成操盤手輕鬆的切換工做軟件以及工做地點。相信不久,咱們也許會在 佛羅里達州、伊維薩島又或者是巴厘島的海灘上看到他們。

應用監聽儀盤表

還有一種常見的用例中,使用 Node+Web+Socket 很適合:跟蹤站點訪問者並且可視化實時它們之間的實時交互。

(假設你有興趣,可以去看看 Hummingbird

你可能需要採集用戶的實時狀態, 或者甚至當他們到達渠道中某個特定的點時, 打開一個交流頻道, 經過有針對性的互動介紹移動到下一個階段. (假設你感興趣的話。推薦你看看 CANDDi

想象一下。假設你知道你的訪客的實時操做。並能夠形象化地看到他們的交互,這將對你的業務帶來多大的提高。隨着實時的、雙向 socket 通訊的 Node.js ,現在你能夠作到了。

系統監控儀表

現在,讓咱們看看事情的基礎設施方面。想象一下,比方,但願爲其用戶提供服務監控頁面(好比,GitHub上的狀態頁)的 SaaS 運營商 。經過 Node.js 的事件循環,咱們可以建立一個基於 Web 的功能強大的儀表板,以異步方式檢查服務狀態並且使用的 WebSockets 將數據推送到client。

內部(公司內部)和公共服務的狀態都可以使用該項技術實現實時的上報。讓咱們把這一想法延伸的遠一點,試着想象一個電信運營商中網絡運營中心(NOC)的監控應用。雲/網絡/server運營商,或者一些金融機構。全都執行在這個由 Node.js 和 WebSocket 組成的應用上,而不是 Java 和/或 Java Applet。

注意:不要嘗試使用 Node 打造硬實時系統(即,響應時間要求一致的系統)。 Erlang是多是該類應用程序的更好的選擇

什麼地方可以使用 Node.js

服務端 WEB 應用

經過 Node.js 使用 Express.js 也可以用來建立服務端上的典型的網頁應用。然而,儘管有可能,使用 Node.js 來進行請求+響應的形式來呈現 HTML 並不是最典型的用例。

有人同意也有人反對這一作法。這裏有一些見解以供參考:

長處:

  • 假設你不需要進行 CPU密集型計算,你可以從頭至尾甚至是數據庫(比方 MongoDB)都使用 Javascript 來開發。這顯著地減輕了開發工序(包含成本)。
  • 對於一個使用 Node.js 做爲服務端的單頁應用或者 websocket 應用,爬蟲可以收到一個全然 HTML 呈現的響應,這是更爲SEO友好的。

缺點:

  • 不論什麼CPU密集型的計算都將阻礙 Node.js 的反應,因此使用多線程的平臺是一個更好的方法。或者,您也可以嘗試向外擴展的計算[*]。
  • Node.js 使用關係型數據庫依然十分痛苦(具體見下方)。

    拜託了。假設你想運行關係型數據操做。請考慮別的環境:Rails, Django 甚至 ASP.NET MVC 。。。。

【*】還有一種解決方式是,爲這些CPU密集型的計算創建一個高度可擴展的MQ支持的環境與後端處理。以保持 Node 做爲一個前臺專員來異步處理client請求。

Node.js 不該該在什麼地方使用

使用關係型數據庫的服務端 WEB 應用

對照 Node.js 上的 Express.js 和 Ruby on Rails。當你使用關係型數據庫的時候請絕不猶豫的選擇後者。

Node.js 的關係數據庫工具仍處於早期階段,眼下尚未成熟到讓人能夠愉快地使用它。

而與此同一時候。Rails天生自帶了數據訪問組件。連同DB schema遷移的支持工具和一些Gems(一語雙關,一指這些如同珍寶的工具,二指ruby的gems程序包)。

Rails和它的搭檔框架們擁有很成熟且被證實了的活動記錄(Active Record)或數據映射(Data Mapper)的數據訪問層的實現,而這些是當你在使用純JavaScript來複制這些應用的時候會很想要使用的東西。

只是,假設你真的傾向於全部使用 JS(並且作好可能抓狂的準備),那麼請繼續關注 Sequelize 和 Node ORM2 。儘管這二者仍然不成熟的。但他們終於會迎頭遇上。

[*] 使用 Node 光是做爲前端而 Rails 作後端來鏈接關係型數據庫,這是全然有可能也並很多見的。(筆者注:國外有種說法。PHP這一類程序猿也可以算做是前端)

繁重的服務端的計算和處理

當涉及到大量的計算,Node.js 就不是最佳的解決方式。你確定不但願使用 Node.js 創建一個斐波那契數的計算服務

普通狀況下,不論什麼 CPU密集型操做 會削弱掉 Node經過事件驅動, 異步 I/O 模型等等帶來的在吞吐量上的優點,因爲當線程被非異步的高計算量佔用時不論什麼傳入的請求將被堵塞。

正如前面所說,Node.js 是單線程的。僅僅使用一個單一的CPU核心。

至於,涉及到server上多核併發處理。Node 的核心團隊已經使用 cluster 模塊的形式在這一方面作了一些工做 (參考:http://nodejs.org/api/cluster.html)。

固然。您也可以很是easy的經過 nginx 的反向代理執行多個 Node.js 的server實例來避免單一線程堵塞的問題。

關於集羣(clustering) ,你應該將所有繁重的計算轉移到更合適的語言寫的後臺進程來處理,同一時候讓他們經過像 RabbitMQ 那樣經過消息隊列server來進行通訊。

即便你的後臺處理可能最初執行在同一臺server上時看不出什麼長處,但是這種作法具備很高的可擴展性的潛力。這些後臺處理服務可以easy地切割出去。做爲單獨的 worker server,而不需要配置入口 webserver的負載。

固然。你也可以在其它語言平臺上用相同的方法,但使用 Node.js 你可以獲得很是高的吞吐量,每個請求都做爲一個小任務很是迅速和高效地處理。這一點咱們已經討論過了。

結論

咱們已經從理論到實踐討論過 Node.js 了,從它的目標和野心,到其長處和缺點。

在 Node.js 的開發中99%的問題是由誤用堵塞操做而形成的。

請記住:Node.js 歷來不是用於解決大規模計算問題而建立的。

它的出現是爲了解決大規模I/O 的問題,並且在這一點上作的很好

綜上,假設你項目需求中不包括CPU密集型操做,也不需要訪問不論什麼堵塞的資源。那麼你就可以利用的 Node.js 的長處,盡情的享受高速、可擴展的網絡應用。

相關文章
相關標籤/搜索