1. 程式人生 > >node.js學習總結

node.js學習總結

NodeJS介紹

1.概述:


      Node.js是基於Chrome JavaScript執行時建立的一個平臺,實際上它是對Google Chrome V8引擎

進行了封裝,它主要用於建立快速的、可擴充套件的網路應用。Node.js採用事件驅動和非阻塞I/O模型,使

其變得輕微和高效,非常適合構建執行在分散式裝置的資料密集型實時應用。

      運行於瀏覽器的Javascript,瀏覽器就是Javascript程式碼的解析器,而Node.js則是伺服器端JS的

程式碼解析器,存於伺服器端的JS程式碼由Node.js來解析和應用。

      JS解析器只是JS程式碼執行的一種環境,瀏覽器是JS執行的一種環境,瀏覽器為JS提供了操作DOM對

象和window物件等介面。Node.js也是JS的一種執行環境,node.js為JS提供操作檔案、建立http服務、

建立TCP、UDP服務等介面,所以Node.js可以完成其他後臺語言能完成的工作。

2.互動式執行環境:PEPL

      Node.js提供了一個互動式執行環境,通過這個環境,可以立即執行JS程式碼,使用方法類似於


Chrome瀏覽器中Firebug外掛中的Console。


      在Linux環境進入終端後,屬於"node"或者“nodejs”進入Node.js的互動式執行環境,Ctrl+d可

以退出此環境。


      檢視系統中安裝的Node.js版本:node -v or nodejs -v

      執行JS檔案,eg:node file.js or nodejs file.js

3.Node.js模組和包

      a.模組

         Node.js官方提供了很多模組,這些模組分別實現了一種功能,如操作檔案模組fs,構建http服


務模組的http等,每個模組都是一個JS檔案,當然也可以自己編   寫模組。


      2.包


          包可以將多個具有依賴關係的模組組織在一起,封裝多個模組,以方便管理。Node.js採用了


CommonJS規範,根據CommonJS規範規定,一個JS檔案就是 一個模組,而包是一個資料夾,包內必須包含


一個JSON檔案,命名package.json。一般情況下,包內bin資料夾存放二進位制檔案,包內的lib資料夾存


放JS檔案,包內的doc資料夾存放文件,包內的test資料夾存放單元測試。package.json檔案中需要包含


的欄位及包的使用。


      3.npm包管理工具

         npm是node.js的包管理工具,npm定義了包依賴關係標準,我們使用npm主要用來下載第三方包

和管理本地下載的第三方包。
========

為什麼我要用 Node.js? 案例逐一介紹


介紹


JavaScript 高漲的人氣帶來了很多變化,以至於如今使用其進行網路開發的形式也變得截然不同了。就


如同在瀏覽器中一樣,現在我們也可以在伺服器上執行 JavaScript ,從前端跨越到後端,這樣巨大的


反差讓人難以想象,因為僅僅在幾年前 Javascript 還如同 Flash 或者 Java applet 那樣嵌入網頁在

沙箱環境中執行。

在深入Node.js之前,你可能需要閱讀和了解使用跨棧式JavaScript(JavaScript across the stack)


帶來的好處,它統一了程式語言和資料格式(JSON),讓你能最佳地重用開發人員資源。由於這更多的


是關於 JavaScript 的特點,這裡就不過多討論它。但它確實是一個讓人在開發環節中使用 Node 的關

鍵的優點。

正如維基百科 所說:“Node.js 是谷歌 V8 引擎、libuv平臺抽象層 以及主體使用 Javscript 編寫的


核心庫三者集合的一個包裝外殼。” 除此之外,值得注意的是,Node.js 的作者瑞恩·達爾 (Ryan 


Dahl) 的目標是建立具有實時推送能力的網站。在 Node.js 中,他給了開發者一個使用事件驅動來實現


非同步開發的優秀解決方案。(注:V8是谷歌開發的,目前公認最快的 Javascript 解析引擎,libuv 是


一個開源的、為 Node 定製而生的跨平臺的非同步 IO 庫。)


簡而言之:Node.js 在實時的 Web應用上採用了基於 WebSocket 的推送技術。這意味著什麼樣的革命性


?Well,在經過了20多年的基於無狀態的請求-返機制的無狀態互動之後,我們終於有了實時的,雙向連


接的web應用,客戶端和伺服器端都可以發起通訊,能夠自由地交換資料。與此形成鮮明對比的是傳統的 


web響應模式,客戶端總是主動發起通訊而服務端被動返回。此外,這些都是基於執行在標準80埠上的


開放Web元件(HTML、CSS和JS)。


可能有人會說,我們已經使用 Flash 和 Java Applet 的形式很多年了——但實際上,這些方式只是使


用網路將資料傳遞到客戶端上的沙箱環境。他們都是隔離執行的,而且經常操作到需要額外的許可權之類


的非標準埠。


憑藉其獨特的優勢,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 一個併發連線的拓展級別 (相關證明).


當然,在所有客戶端的請求共享單一執行緒時也會有問題, 這也是一個編寫 Node.js 應用的潛在缺陷. 首


先, 大量的計算可能會使得 Node 的單執行緒暫時失去反應, 並導致所有的其他客戶端的請求一直阻塞, 


直到計算結束才恢復正常。 其次,開發人員需要非常小心,不要讓一個 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 的客戶端函式庫.
coffee-script – CoffeeScript 編譯器,允許開發者使用 Coffee 來編寫他們的 Node.js 程式。
underscore (lodash, lazy) – 最流行的 JavaScript 工具庫 , 用於 Node.js 的封裝包,以及兩個採


取略有不同的實現方法來獲得更好效能的同行。
forever – 可能是用來確保 node 指令碼持續執行的最流行的工具。
還有很多好的模組,這裡就不一一列舉了(希望沒有冒犯到沒列舉的)。


Node.js 應該用在什麼地方



聊天


聊天是最典型的多使用者實時互動的應用。從 IRC 開始,有許多開源或者不開源的協議都執行在非標準端

口上,而現在,使用 Node.js 則可以解決這些問題——在標準的80埠執行 WebSockets。

聊天應用程式是最能體現 Node.js 優點的例子:輕量級、高流量並且能良好的應對跨平臺裝置上執行密

集型資料(雖然計算能力低)。同時,聊天也是一個非常值得學習的用例,因為它很簡單,並且涵蓋了

目前為止一個典型的 Node.js 會用到的大部分解決方案。

讓我們試著來描繪它如何工作。


在最簡單的情況下,我們佈置了一個聊天室在我們的網站上,使用者可以在上面發訊息,當然是一對多的

形式。例如,假設總共有三個人連線到我們的網站上。

在服務端這邊, 我們有一個使用 Express.js 搭建的簡單站點,該站點實現了兩件事 1) 處理路徑為 

‘/’ 的GET請求時,下發包括一個留言板以及一個傳送資訊的 ‘傳送’ 按鈕的頁面 2) 一個監聽客戶

端傳送新訊息的 websockets 服務。

在客戶端這邊,我們有一個 HTML 頁面,上面有個兩個 js 方法,一個是用於觸發事件的 “傳送” 按

鈕,這會把把輸入的訊息通過 webscoket 傳送,另一個方法是用 webscoket 在客戶端上監聽服務端來

的推送(例如,其他使用者傳送的訊息)。

當有一個客戶端傳送訊息的時候,發生的事情是:


瀏覽器上,點擊發送按鈕觸發了 js 函式,將輸入框中的文字通過 websocket 訊息傳送到伺服器的 


websocket 客戶端(頁面初始化載入的時候連線的)。
服務端的 websocket 元件收到 訊息,然後通過廣播方法轉發到其他所有連線的客戶端。
通過頁面上執行的 websocket 客戶端元件,所有的客戶端都能收到這條推送的新訊息。接著 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 物件以供客戶端使用。另外,從資料庫讀寫時候如果使用的是 

MongoDB 的話,你也不用擔心的 JSON 與任何資料之間的格式問題。總之,你可以避免多元的資料轉換

問題,不論是在客戶端、服務端還是資料庫。

佇列輸入



如果你正在接收一個高量併發的資料,你的資料庫可能會成為你處理的瓶頸。正如上面的描述,Node.js 


可以輕鬆的處理併發連線。 但是,由於資料庫操作是一個阻塞的操作(在這種情況下),這就是麻煩的


地方。Node.js的解決方案是,在資料真正的寫入之前就承認客戶端的資料是真實的。


用這種方法,在高負載的時候系統繼續維持它的響應,這在當客戶端不需要嚴格確認一個數據是否成功


的被寫入時特別有用。典型的例子包括:日誌記錄或者使用者跟蹤資料(user-tracking data)的記錄,


這會被分批處理並且在稍後才使用;同時也包括最終一致性(so, 常用於 NoSQL)可以接受,不需要立


即反應的操作(例如 Facebook 上更新點讚的數目)。


資料通過某些快取或者訊息佇列的基礎元件(例如 RabbitMQ, ZeroMQ)進入佇列,並且通過一個獨立的


資料庫批量寫入程序來一一消化,或者通過一個更高效能的計算密集型後端服務來進行處理。其他的語


言/框架也可以實現相似的操作,但在相同的配置下是達不到 nodejs 的高吞吐量與高併發。


toptal-blog-3_B


簡單的說:使用 Node,你可以把資料庫操作扔到一邊並在稍後處理它們,假設他們成功了一樣繼續執行


下去。(筆者注:在開發中通常的情況通常是,種耗時的操作通過回撥函式來非同步處理,主執行緒繼續往


下執行)


資料流



在較為傳統的網路平臺上,HTTP 的請求和響應更像是孤立的事件;然而事實上,他們都是資料流。這一


觀察結果在 Nodejs 上可以用來建立一些很酷的功能。因為資料通以流的形式接收,而我們可以在網站


上線上處理正在上傳中的檔案。這樣的話,就可以實現實時的音訊和視訊編碼,以及在不同資料來源之間


進行程式碼(代理見下一段)。


(筆者注:Node 有代替如 apache 這樣的 webserver 處理資料,所以開發者可以直接收到客戶端一份


一份上傳的資料,並實時處理。上面這段話聽起來有點抽象,不過各位可以簡單的想象一下不需要開 YY 


或者 QQ,開啟網頁就能進行語音視訊的功能。)


代理



Node.js 可以通過非同步的方式處理大量的併發連線,所以很容易作為服務端的代理來使用。這在與不同


響應時間的不同服務之間進行代理,或者是收集來自多個來源的資料時尤其有用。


舉個例子:考慮一個伺服器端的應用程式和第三方資源進行通訊以更新自不同來源的資料,或者將服務


端上的一些影象和視訊資源儲存到第三方雲服務。


雖然專用代理伺服器確實存在,但是如果你還沒有專用的代理伺服器,或者你需要一個本地開發的解決


方案,那麼使用 Node 來做代理可能是更好的選擇。關於這個解決方案,我的意思是指當你在開發的時


候,你可以使用Node.js的開發環境搭建一個服務來處理對資源和代理的請求,而在生產環境下,你可以


使用專用的代理服務(比如nginx,HAProxy等)來處理這些互動。


股票操盤手的儀表盤



讓我們繼續討論應用程式這塊。實時網路的解決方案可以很輕鬆的實現證券交易軟體——用於跟蹤股票


的價格,執行計算、做技術分析,同時生成報表。


使用一個實時的的基於網頁的解決方案,將會允許操盤手輕鬆的切換工作軟體以及工作地點。相信不久


,我們或許會在 佛羅里達州、伊維薩島又或者是巴厘島的海灘上看到他們。


應用監聽儀盤表



另一種常見的用例中,使用 Node+Web+Socket 非常適合:跟蹤網站訪問者並且視覺化實時它們之間的實


時互動。 (如果你有興趣,可以去看看 Hummingbird)


你可能需要採集使用者的實時狀態, 或者甚至當他們到達渠道中某個特定的點時, 開啟一個交流頻道, 通


過有針對性的互動介紹移動到下一個階段. (如果你感興趣的話,推薦你看看 CANDDi)


想象一下,如果你知道你的訪客的實時操作,並能夠形象化地看到他們的互動,這將對你的業務帶來多


大的提升。隨著實時的、雙向 socket 通訊的 Node.js ,現在你可以做到了。


系統監控儀表


現在,讓我們看看事情的基礎設施方面。想象一下,比如,希望為其使用者提供服務監控頁面(例如,

GitHub上的狀態頁)的 SaaS 運營商 。通過 Node.js 的事件迴圈,我們可以建立一個基於 Web 的功能

強大的儀表板,以非同步方式檢查服務狀態並且使用的 WebSockets 將資料推送到客戶端。

內部(公司內部)和公共服務的狀態都可以使用該項技術實現實時的上報。讓我們把這一想法延伸的遠

一點,試著想象一個電信運營商中網路運營中心(NOC)的監控應用,雲/網路/伺服器運營商,或者一些

金融機構,全都執行在這個由 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 作為一個前臺專員來非同步處理客戶端請求。


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核心。至於,涉及到伺服器上多核併發處理

,Node 的核心團隊已經使用 cluster 模組的形式在這一方面做了一些工作 (參考:

http://nodejs.org/api/cluster.html)。當然,您也可以很容易的通過 nginx 的反向代理執行多個 

Node.js 的伺服器例項來避免單一執行緒阻塞的問題。

關於叢集(clustering) ,你應該將所有繁重的計算轉移到更合適的語言寫的後臺程序來處理,同時讓他

們通過像 RabbitMQ 那樣通過訊息佇列伺服器來進行通訊。

即使你的後臺處理可能最初執行在同一臺伺服器上時看不出什麼優點,但是這樣的做法具有非常高的可

擴充套件性的潛力。這些後臺處理服務可以容易地分割出去,作為單獨的 worker 伺服器,而不需要配置入

口 web伺服器的負載。

當然,你也可以在其他語言平臺上用同樣的方法,但使用 Node.js 你可以得到很高的吞吐量,每個請求


都作為一個小任務非常迅速和高效地處理,這一點我們已經討論過了。

結論


我們已經從理論到實踐討論過 Node.js 了,從它的目標和野心,到其優點和缺點。在 Node.js 的開發

中99%的問題是由誤用阻塞操作而造成的。

請記住:Node.js 從來不是用於解決大規模計算問題而建立的。它的出現是為了解決大規模I/O 的問題

,並且在這一點上做的非常好。

綜上,如果你專案需求中不包含CPU密集型操作,也不需要訪問任何阻塞的資源,那麼你就可以利用的 


Node.js 的優點,盡情的享受快速、可擴充套件的網路應用。
========

一個Java碼農的Node之旅



本篇是一個Node新手做完實際專案後的心得總結。


摘要
如果BOSS要求你在短期內快速實現一套聊天雲服務平臺,實現成本主要是:


要維護社交關係,一大波殭屍POJO正在襲來。
要儲存資料庫,找個ORM工具。
長連線吧方面是WebSocket標準,亦或Netty、Mina系的框架。
連線斷了需要實現下重連機制吧?伺服器端寫完了還有客戶端,也需要幫助指導下實現,問題諸多。
我們的征途是偷懶
如果有現成的輪子偷懶的話,socket.io比較適合:


輕量級、擴充套件便捷、API簡單易用。
周邊完善,重連、路由、隔離、單播、廣播等都已實現。
豐富的客戶端支援,涵蓋了瀏覽器端、Android、iOS。
在仔細研讀了Flexi傳授如何說服自己的老闆採用Node.js,併成功說服BOSS後,正式開始自己的Node之


旅。


工欲善其事必先利其器
開發環境
對於開發環境,無論你是使用編輯器/編輯器之神,或是sublime/atom/npp之類,亦或是WebStorm高富帥


有錢任性,都是很不錯的選擇。


Supervisor可以幫你watch程式碼變更,自動重啟服務,從而節省了手工重啟程式的時間。


關於除錯
高富帥款:WebStorm
高逼格款:原始打斷點
屌絲款:node-inspector,可以在Chrome中直接除錯,強烈推薦:
圖片描述


離不開的中介軟體
首先,勾勒出一個核心的聊天系統大致的雛形:


基本功能


叢集實現
敏感詞過濾
好友


加好友,刪好友
好友之間聊天,發文字發圖片發音頻發視訊啥的
群組


建立,加入,退出群組
群組內廣播聊天
聊天曆史記錄


擴充套件與周邊


叢集實現
敏感詞過濾
分析下大致需要的儲存層和中介軟體以及是否有相關的Node實現:


MySQL:儲存一些重要的元資料,主要是使用者關係類的,需要事務支援。(node-mysql)
ZooKeeper:使用者線上離線狀態儲存。(node-zookeeper-client)
Redis:使用快取加速一些查詢,PubSub特性用於實現叢集通訊。(ioredis)
HBase:典型的列式儲存,用於實現一些非核心資料的快速儲存查詢。(hbase-rpc-client)
LevelDB:本地快速讀寫一些鍵值對。(LevelUP)
技術棧
對比


常年滋潤在Java這片潤土之上,先來做個比較,讓我們對陌生的技術棧有所瞭解。


圖片描述


ES6
ES6是個好東西, 我覺得比較好用的三點:


const: 可以方便地對不可變的東西進行宣告。
let: 只JavaScript初學者不用擔憂不知不覺把變數提升的問題。
lambda表示式: 神器。
核心實現
流程時序
仔細思考,勾勒出大致的時序圖:


圖片描述


系統架構:
設計下大致的架構:


圖片描述


步步為贏,各個擊破
狀態管理


典型的IM系統中必然存在使用者線上離線的狀態。每一個線上狀態,對於伺服器來說,等價於與客戶端存


在一個Socket連線。所以對於單機環境下,在記憶體中維護使用者和Socket的關係即可,當Socket連線和斷


開時分別做更新操作。當切換至叢集環境時,情況變得稍微複雜,所以我們需要藉助ZooKeeper來實現。


除了本機每個使用者與Socket關聯關係,另外以臨時節點的方式在ZooKeeper中進行儲存,目錄結構為根節


點/名稱空間/使用者標識/Socket標識(臨時節點)。當socket連線被建立的時候,建立對應的臨時節點,


socket斷開時移除臨時節點。當伺服器意外退出時,除了socket連線全部斷開之外,在其ZooKeeper 


session上的所有對應的臨時節點也會被銷燬。SocketIO的重連機制會嘗試重連至其他伺服器並重新建立


起對應關係。


優點:


判斷一個使用者是否線上只需判斷使用者標識節點的numChildren是否大於零即可。
獲取使用者所有已連線的Socket只需讀取使用者標識下的所有孩子節點即可。
缺點:


多了額外讀寫ZooKeeper的開銷。
用途:


實現叢集的基礎
有了狀態判定才能實現離線訊息推送
好友關係、群組關係


關係表原本儲存於HBase,但因為缺乏事務支援,實際效果不佳,經常導致關係不一致。傳統關係型資料


庫在這一方面依舊強勢。這塊比較簡單,即常見的關係模型表,在此略過。


點對點聊天實現


單機


A對B傳送訊息,除了基礎的許可權判定,只需查詢記憶體表中對應B的所有socket,然後對其發射訊息即可。


見下圖:


圖片描述


叢集


由於B可能登入在不同的伺服器上,需要藉助訊息中介軟體(Redis Pub/Sub),釋出訊息,每個伺服器訂閱


訊息列表,如果存在訊息接收者,則找到其註冊在本機的socket進行發射訊息。流程如下圖:


圖片描述


廣播聊天實現


廣播類似以上的點對點實現,只是多了一步查詢成員表依次處理的步驟。此處略。


聊天曆史記錄的實現


考慮到只需要根據時間範圍做分頁查詢的簡單需求,這裡使用了HBase的寬表。點對點形式的聊天我們可


以對兩個使用者標識進行排序,並結合名稱空間生成唯一的雜湊值,作為行健,而每個CELL的值則是時間


戳,因為我們需要令其自然倒序排列,所以針對時間戳做了LONG。MAX-時間戳的處理。綜合起來,大致


的儲存結構如下:


圖片描述


敏感詞過濾


維護髒詞字典,對訊息進行字串替換?為了實現正確辨認“曹操在操場操美女”中的動詞“操”,需


要實現中文分詞和詞性判斷,於是處理邏輯轉變成:


拉取最新的髒詞列表,轉換為簡體中文並寫入LevelDB中。
使用nodejieba進行中文分詞:曹操(n)/在(p)/操場(n)/操(v)/美女(n)
對分詞後的名詞和動詞轉換為簡體中文並查詢LevelDB,命中則替換。
返回替換後的字串得到:曹操在操場*美女
打包部署
PM2
Node本身是單執行緒的,雖然Node本身提供Cluster模組,但需要修改程式碼。通過PM2這個工具可以簡便地


讓其多程序部署,充分利用多核CPU資源:


圖片描述


Docker
可以使用官方的node映象。但體積比較大,這裡推薦基於alpine-node,體積比較小巧,例如:


FROM mhart/alpine-node:4


RUN apk add --no-cache make gcc g++ python


RUN apk add --no-cache imagemagick


WORKDIR /src
ADD . .
RUN npm install --registry=http://registry.npm.taobao.org/


EXPOSE 3000
CMD ["npm","start"]
C1000K測試
著名的單機100萬連線,由於專案是第一個版本,限於各方面原因我們暫時沒有完成這個測試。但在這裡


簡短介紹所需的一些配置,以備後續使用:


伺服器端
修改tcp連線的最小記憶體為4k, 編輯/etc/sysctl.conf
net.ipv4.tcp_wmem = 4096 87380 4161536
net.ipv4.tcp_rmem = 4096 87380 4161536
net.ipv4.tcp_mem = 786432 2097152 3145728
修改系統最大檔案描述符, 編輯/etc/sysctl.conf
fs.file-max = 1000000
修改程序最大檔案描述符, 編輯/etc/security/limits.conf
*         hard    nofile      1000000
*         soft    nofile      1000000
root      hard    nofile      1000000
root      soft    nofile      1000000
過載下配置(sysctl -p)或者重啟,檢查下當前的設定 cat /proc/sys/fs/file-nr
*客戶端


因為每個IP最多可以建立6萬多個連線,不可能找很多伺服器進行測試。所以客戶端除了上述修改,還需


要建立多個虛擬IP,這樣每個IP可以提供大約6萬的連線,如:
 ifconfig eth0:0 192.168.77.10 netmask 255.255.255.0 up
 ifconfig eth0:1 192.168.77.11 netmask 255.255.255.0 up
 ifconfig eth0:2 192.168.77.12 netmask 255.255.255.0 up
 ifconfig eth0:3 192.168.77.13 netmask 255.255.255.0 up
 ifconfig eth0:4 192.168.77.14 netmask 255.255.255.0 up
 ifconfig eth0:5 192.168.77.15 netmask 255.255.255.0 up
 ifconfig eth0:6 192.168.77.16 netmask 255.255.255.0 up
 ifconfig eth0:7 192.168.77.17 netmask 255.255.255.0 up
 ifconfig eth0:8 192.168.77.18 netmask 255.255.255.0 up
 ifconfig eth0:9 192.168.77.19 netmask 255.255.255.0 up
 ifconfig eth0:10 192.168.77.20 netmask 255.255.255.0 up
 ifconfig eth0:11 192.168.77.21 netmask 255.255.255.0 up
 ifconfig eth0:12 192.168.77.22 netmask 255.255.255.0 up
 ifconfig eth0:13 192.168.77.23 netmask 255.255.255.0 up
 ifconfig eth0:14 192.168.77.24 netmask 255.255.255.0 up
 ifconfig eth0:15 192.168.77.25 netmask 255.255.255.0 up
 ifconfig eth0:16 192.168.77.26 netmask 255.255.255.0 up
 ifconfig eth0:17 192.168.77.27 netmask 255.255.255.0 up
 ifconfig eth0:18 192.168.77.28 netmask 255.255.255.0 up
修改本地埠範圍,編輯/etc/sysctl.conf
    net.ipv4.ip_local_port_range = 1024 65535
過載配置或重啟開始測試
總結
存在即合理,不要捲入無謂的語言之爭,本猿覺得幹這行的最重要莫過於學習能力。 
寫程式碼之前先理清楚思路和結構,不打沒有準備的仗。


良好的程式碼規範,遵循KISS原則。
========

相關連結


http://www.yiibai.com/nodejs/
http://www.runoob.com/nodejs/nodejs-tutorial.html
http://nodejs.cn/
http://blog.jobbole.com/53736/

http://www.csdn.net/tag/Node/news