爲何要用Node.js

翻譯:瘋狂的技術宅

原文:https://medium.com/the-node-j...javascript

未經許可,禁止轉載!html


本文首發微信公衆號:前端先鋒
歡迎關注,天天都給你推送新鮮的前端技術文章前端


介紹

JavaScript 的日益發展帶來了不少變化,當今的 Web 開發面貌已經變得大相徑庭。在幾年前是很難想象在服務器上運行 JavaScript 的。java

在深刻研究Node.js以前,你可能想了解使用跨棧的 JavaScript 有什麼好處,它統一了語言和數據格式(JSON),容許你以最佳的方式重用開發人員資源。將 Node.js 合併到技術棧中是一個關鍵優點。node

Node.js 是一個基於 Chrome 的名爲 V8 的 JavaScript 引擎構建的 JavaScript 運行環境。值得注意的是,Node.js 的建立者 Ryan Dahl 的「受到 Gmail 等應用的啓發」,目標是爲了開發一個具備實時推送功能的網站。在 Node.js 中,他提供了一個用於處理非阻塞事件驅動的 I/O 工具。nginx

用一句話來歸納:Node.js 在基於websockets 推送技術的實時 Web 應用中大放異彩。在過去的 20 多年來咱們一直在使用基於無狀態請求 - 響應模式的無狀態 Web 應用,如今終於擁有了可以實時雙向鏈接的 Web 應用,其中客戶端和服務器均可以啓動通訊,並容許它們自由地交換數據。git

這與典型的老是由客戶端發起通訊的 Web 響應模式形了成鮮明的對比。此外它也一樣基於在標準端口 80 上運行的開放 Web 技術棧(HTML,CSS和JS)。程序員

有人可能會爭辯說,咱們多年來一直以 Flash 和 Java Applet 的形式作到這一點 —— 但實際上,這些只是使用 Web 做爲傳輸協議將數據傳給客戶端的沙盒環境。此外,它們是隔離運行的,一般在非標準端口上運行,這可能須要額外的權限。github

憑藉其優點,Node.js 在依賴其獨特優點的衆多知名公司的技術堆棧中發揮着關鍵做用。 Node.js 基金會幾乎已經整合了全部最好的想法,能夠在 Node.js 基金會的案例研究頁面上找到關於爲何企業應該考慮 Node.js 的簡短PPT。web

在本文中,我將不只要討論如何使用這些優點,並且還要討論 爲何 你可能想要使用 Node.js ,並用一些經典的 Web 應用程序模型做爲示例。

它是如何工做的?

Node.js 的主要思想是:在面向跨分佈式設備運行的數據密集型的實時程序時,使用非阻塞、事件驅動的 I/O 來保證輕量和高效。

這讀起來很拗口。

這意味着 Node.js 不是 一個即將成爲主宰 Web 開發界的可以解決一切的新平臺。 相反,它是一個知足特定需求的平臺。理解這一點絕對是有必要的。你毫不但願將 Node.js 用於 CPU 密集型的操做;實際上,將它用於進行大量繁重運算的場合將會消除它幾乎全部的優勢。 Node.js 真正發揮做用的地方在於構建快速、可擴展的網絡應用,由於它可以以高吞吐量處理大量併發鏈接,這至關於具備高可擴展性。

其底層的工做原理很是有趣。傳統的 Web 服務技術每一個鏈接(請求)都會產生一個新線程,佔用系統內存並最終受限於可用的最大內存,而 Node.js 在單線程上運行,使用非阻塞 I/O 調用,容許它支持數以萬計的併發鏈接(在 event loop 中維持)。

clipboard.png

快速計算:假設每一個線程須要 2 MB 內存,那麼在有 8 GB 內存的系統上運行的話,理論上最多有 4000 個併發鏈接(計算來自 Michael Abernethy 的文章 「Just what is Node.js?「,2011年在 IBM developerWorks 上發佈;不幸的是,這篇文章的連接如今已經失效了),這尚未算上線程之間的上下文切換的成本。這就是你一般在傳統的 Web 服務器技術中處理的場景。經過避免全部這些問題,Node.js 實現了超過 1M 鏈接併發數的級別,以及 600k 的 websockets 併發鏈接數

固然,編寫 Node.js 應用的潛在缺陷是存在客戶端請求之間共享單個線程的問題。首先,繁重的計算可能會阻塞 Node 的單個線程並致使 全部 客戶端出現問題(稍後會詳細說明),由於傳入的請求將被阻塞,直到計算完成爲止。其次開發人員須要 很是當心,不要讓異常冒泡到到核心(最頂層)Node.js 事件循環,這將致使 Node.js 實例終止(程序崩潰)。

爲了不異常冒泡到頂層,經常使用技術是將錯誤做爲回調參數傳遞迴調用者(而不是像在其餘環境中那樣拋出它們)。即便一些未被處理的異常冒泡到頂層,也有一些工具來監視 Node.js 進程並執行必要的恢復崩潰 (雖然可能沒法恢復到用戶會話的當前狀態),最多見的是 Forever 模塊

npm:node 包管理器

在討論 Node.js 時,一件絕對不該該被忽略的事是支持使用內置的 npm 工具進行包管理,默認狀況下每一個 Node.js 環境都會安裝。 npm 模塊的概念很是相似於 Ruby Gems:一組可經過在線存儲庫輕鬆安裝,具備版本和依賴關係管理的可重用組件,。

能夠在 npm 網站上找到已打包模塊的完整列表,也可使用自動與 Node.js 一塊兒安裝的 npm CLI 工具進行訪問。模塊生態系統對全部人開放,任何人均可以發佈本身的模塊,發佈的模塊將出如今 npm 存儲庫中。有關 npm 的簡介,請參閱初學者指南,以及 npm 發佈教程中關於發佈模塊的部分。

一些頗有用的 npm 模塊是:

  • express —— Express.js,一個受 Sinatra 啓發的 Node.js Web 開發框架,當今大多數 Node.js 應用程序的事實標準。
  • hapi —— 一個模塊化的且很是易於使用的以配置爲中心的框架,用於構建 Web 和服務應用
  • connect —— Connect 是 Node.js 的可擴展 HTTP 服務器框架,提供了一系列稱爲中間件的高性能「插件」做爲Express的基礎。
  • socket.iosockjs —— 今天最多見的兩個 websockets 服務器端組件。
  • pug(之前叫 Jade)—— 受 HAML 啓發的流行模板引擎之一,Express.js 中的默認選項。
  • mongodbmongojs —— MongoDB 包裝器,爲 Node.js 中的 MongoDB 對象數據庫提供 API。
  • redis —— Redis 客戶端。
  • forever —— 多是確保給定 node 腳本連續運行的最經常使用實用程序。在遇到意外故障時,將 Node.js 的進程保持在生產狀態。
  • bluebird —— 功能齊全的 Promises/A+ 實現,性能很是出色
  • moment —— 用於解析、驗證、操做和格式化日期的輕量級 JavaScript 日期庫。

列表還在不斷增加。那裏有不少有用的包,可供全部人使用。

哪些場合應該使用 Node.js

在線聊天

在線聊天是最典型的實時多用戶應用,也是 Node.js 的最佳案例:它是一個輕量級、高流量、數據密集型(可是低處理和計算)的應用程序,可分佈式跨設備運行。它也是一個很好的學習案例,由於它很簡單,但涵蓋了你在典型的 Node.js 程序中所使用的大部分範例。

讓咱們試着描繪它是如何工做的。

假設一個最簡單的場景,在咱們的網站上有一個聊天室,人們能夠經過一對多(其實是對全部人)的方式交換消息。

在服務器端,咱們有一個簡單的 Express.js 程序,它實現了兩件事:1) 一個GET 請求的處理程序,它提供了包含留言板和用於初始化新消息輸入的「發送」按鈕的功能,以及2) 用於偵聽 websocket 客戶端發出的新消息的w ebsockets 服務器。

在客戶端,咱們有一個 HTML 頁面,其中設置了幾個處理程序,一個用於「發送」按鈕的單擊事件,它接收輸入消息並將其發送到 websocket,另外一個用於偵聽新的傳入消息並顯示在 websockets 客戶端上(即服務器但願客戶端顯示的其餘用戶發送的消息)。

當其中一個客戶發佈消息時,會發生如下狀況:

  • 瀏覽器捕獲單擊「發送」按鈕事件處理 JavaScript 程序,從輸入字段(即消息文本)中獲取值,並使用鏈接到咱們服務器的 websocket 客戶端發出 websocket 消息(在網頁初始化時初始化) 。
  • websocket 鏈接的服務器端組件接收消息,並使用廣播方式將其轉發給全部其餘的客戶端。
  • 全部客戶端都經過在網頁中運行的 websockets 客戶端組件接收新消息。而後,他們經過將新消息添加頁面上並更新。

clipboard.png

這是最簡單的例子。對於更強大的解決方案,你可使用基於 Redis 的簡單緩存。或者在更高級的解決方案中,能夠用消息隊列做爲消息路由,還能夠實現更強大的傳遞機制,例如能夠在鏈接丟失或在客戶端脫機時存儲消息。但不管你作出哪些改進,Node.js 仍將按照相同的基本原則運行:對事件作出反應,處理許多併發鏈接,並保持用戶體驗的流暢性。

對象數據庫頂層的 API

雖然 Node.js 的確很適合開發實時應用,但它也很適合從對象數據庫(例如MongoDB)公開數據。 JSON 存儲的數據容許 Node.js 在對象與存儲數據一致和沒有數據轉換的狀況下良好的運行。

例如,若是你正在使用 Rails,那麼你須要從 JSON 轉換爲二進制模型,而後經過 HTTP 再將它們轉爲 JSON 在 React.js 或 Angular.js 中使用 ,甚至能夠用簡單的 jQuery AJAX 進行調用。使用 Node.js,你能夠經過 REST API 直接公開你的 JSON 對象來供客戶端使用。此外,在從數據庫讀取或寫入時(若是你使用的是MongoDB),你無需擔憂在 JSON 和其餘任何內容之間進行轉換的問題。總之在客戶端、服務器和數據庫中使用統一的數據序列化格式,能夠避免屢次轉換的麻煩。

隊列輸入

若是你收到了大量併發數據,那麼你的數據庫可能會成爲瓶頸。如上所述,Node.js 能夠輕鬆地本身處理併發鏈接。可是由於數據庫訪問是一種阻塞操做(在這種狀況下),因此咱們遇到了麻煩。解決方案是在數據真正寫入數據庫以前先確認客戶端的行爲。

經過這種方法,系統能夠在高負載下保持其響應性,這在客戶端不須要確認數據成功寫入時尤爲有用。典型的例子包括:記錄或寫入用戶跟蹤數據時進行分批處理;以及最終一致性(常常在NoSQL世界中使用)能夠接受的不須要當即做出反映的操做(例如更新 Facebook 上的「Likes」計數)。

數據經過某種緩存或消息隊列(例如,RabbitMQ,ZeroMQ)排隊,並經過單獨的數據庫批量寫入過程,或者由計算密集型後端服務進行消化,再寫入更好的可以執行此類任務的平臺。相似的行爲能夠用其餘語言或框架實現,但不能在相同的硬件上實現,以維持相同的高吞吐量。

clipboard.png

簡而言之:使用 Node,你能夠將數據庫寫先入到一個地方,稍後再去處理它們,就像它們已經被成功處理同樣。

數據流

在更傳統的Web平臺中,HTTP 請求和響應被看做是孤立事件,實際上他們是流。能夠在 Node.js 中使用這個性質來構建一些很酷的功能。例如文件能夠被一邊上傳一邊處理,由於數據經過流進入,咱們能夠實時的去處理它。這能夠用於實時音頻視頻編碼,以及在不一樣數據源的之間進行代理(參見下一部分)。

代理

把 Node.js 用做服務器端代理是很容易的,它可以以非阻塞方式處理大量的併發鏈接。這對於爲代理不一樣響應時間的多個服務,或從多個源收集數據的場景特別有用。

例如如下場景:當服務器端程序與第三方資源進行通訊時,會從不一樣的來源提取數據,或者將圖像和視頻等資源存儲到第三方雲服務上。

儘管有專用代理服務器,可是若是你沒有基礎的代理架構,或者你須要本地開發環境,那麼 Node 可能會對你有所幫助。

股票交易商的數據界面

讓咱們回到應用程序。能夠很容易地用實時網絡解決方案取代的另外一個例子是股票經紀人的交易軟件,它用於跟蹤股票價格、執行計算、技術分析以及建立圖表。

若是切換到基於 Web 的實時解決方案,經紀人將能夠輕鬆切換工做站或工做場所。很快,咱們可能會開始在佛羅里達州的海灘上看到它們......

應用監控儀表板

另外一個常見的用例,其中 Node-with-web-socket 徹底適合:跟蹤網站訪問者並對他們的交互進行實時的可視化。你能夠從用戶那裏實時收集統計信息,甚至能夠經過在訪問渠道中特定的點來打開通訊渠道,並與訪問者進行有針對性的互動,這種方案能夠在這裏找到: CANDDi

想象一下,若是你可以實時瞭解訪問者所作的事情,你將如何改善你的業務呢?經過使用 Node.js 的實時雙向套接字,如今就能夠作到了。

系統監控儀表板

在基礎設施方面,。好比想要爲其用戶提供服務監控頁面的SaaS提供商(例如,GitHub狀態頁面)。經過 Node.js 事件循環,咱們能夠建立一個功能強大的基於 Web 的儀表板,以異步方式檢查服務的狀態,並使用 websockets 將數據推送到客戶端。公司內部和公共服務的狀態均可以使用該技術獲得實時報告。

注意:不要嘗試在 Node.js 中構建硬實時系統(即須要一致響應時間的系統)。對於那類應用程序,Erlang 多是更好的選擇

哪些場合可使用 Node.js

服務器端 Web 應用

配合 Express.js 的 Node.js 也可在服務器端建立經典 Web 應用。對於這種方法,有人支持也有人反對。如下是一些須要考慮的問題:

優勢:

  • 若是你的程序沒有任何 CPU 密集型計算,能夠用 Javascript 和對象存儲數據庫(如MongoDB)構建它,甚至能夠在數據庫級別進行構建。這顯著的簡化了開發工做。
  • 爬蟲會收到一個可以徹底呈現的 HTML 響應,這比單頁應用或在 Node.js 上運行的 websockets 應用程序更能進行 SEO 。

缺點:

  • 任何 CPU 密集型計算都會阻止 Node.js 響應,所以線程平臺是一種更好的方法。
  • 將 Node.js 與關係數據庫放一塊兒使用仍然很是困難(更多細節見下文)。若是你要對關係型數據庫進行操做,請並選擇 Rails、Django 或 ASP.Net MVC 等其餘環境。

CPU 密集型計算的一種替代方法是建立一個可高度擴展的 MQ 支持環境,該環境具備後端處理功能,以使 Node 成爲一個前臺「職員」,並以異步方式處理客戶端請求。

何時不該使用 Node.js

帶有關係型數據庫的服務器端 Web 應用

例如,將 Node.js + Express.js 與 Ruby on Rails 進行比較,當涉及到關係數據訪問時,顯而後者更合適。

與其競爭對手相比,Node.js 的關係型數據庫工具仍然至關原始。另外一方面,Rails 提供了開箱即用的數據訪問設置以及數據庫架構遷移支持工具,另外還有其餘的 Gems。 Rails 及相似框架擁有成熟的且通過驗證的 Active RecordData Mapper 數據訪問層實現,若是你想要嘗試在純 JavaScript 中複製這些功能的話,那麼祝你好運。

不過,若是你真的傾向於用 JS 實現一切,請查看 SequelizeNode ORM2

若是僅僅是把 Node.js 用做面向公衆的界面,同時用 Rails 後端訪問關係數據庫,這是能夠的,並且這種方式並不罕見。

繁重的服務器端計算與處理

當涉及到繁重的計算時,Node.js 並非最好的平臺。你絕對不想用 Node.js 去構建一個 Fibonacci 計算服務器。一般,任何 CPU 密集型操做都會經過事件驅動的非阻塞 I/O 模型來抵消 Node 提供的全部吞吐量優點,由於當線程被數字運算佔用時,任何傳入請求都將被阻止。

正如前面所說的,Node.js 是單線程的,只使用一個CPU核心。在多核服務器上添加併發性時,Node 核心團隊以 cluster module 的形式完成一些工做。你也能夠很容易地在反向代理 nginx 的後面運行幾個 Node.js 服務器實例。

若是使用羣集,你仍然應該將全部繁重的計算放到在更合適的環境下編寫的後臺進程中,並使它們經過像 RabbitMQ 這樣的消息隊列服務器進行通訊。

即便你全部的後臺處理最初可能在同一服務器上運行,這種方法也有可能實現很是高的可伸縮性。這些後臺處理服務能夠輕鬆地被分發到單獨的工做服務器,而無需對前置 Web 服務器負載進行配置。

固然,你也能夠在其餘平臺上使用相同的方法,可是使用 Node.js,你能夠得到咱們所討論的高 reqs/sec 吞吐量,由於每一個請求都是一個很是快速有效的小任務。

結論

咱們討論了 Node.js 從理論到實踐,從它的目標和抱負開始,並以其最佳點和陷阱結束。當人們遇到 Node 的問題時,它幾乎老是唄歸結爲阻塞操做是全部邪惡的根源 —— 其中 99% 的直接緣由是對 Node 的誤用。

請記住:不要用 Node.js 來解決計算擴展問題。它是爲了解決 I/O 擴展問題而設計的,它作得確實很好

因此,若是你的應用不包含 CPU 密集型操做,也不訪問任何阻塞資源的話,能夠利用 Node.js 的優點,享受快速、可擴展的網絡應用。


本文首發微信公衆號:前端先鋒

未經許可,禁止轉載!

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章


歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索