構(gòu)建Node.js應(yīng)用的10個重要的方面

2013-03-08 10:28:19來源:開源中國作者:

應(yīng)用程序需要有個明確目標(biāo),能幫干活能解決問題。這一點同樣可以用于你決定去做的每件事上。目標(biāo)明確根基夯實,應(yīng)用程序才會越做越好。明確的目標(biāo)會幫你闖出出一條如何正確解決問題的道路、也讓你在遇到阻礙陷入困境無法前行的時候依然堅定執(zhí)著著望向遠(yuǎn)方。

目標(biāo)

應(yīng)用程序需要有個明確目標(biāo),能幫干活能解決問題。這一點同樣可以用于你決定去做的每件事上。目標(biāo)明確根基夯實,應(yīng)用程序才會越做越好。明確的目標(biāo)會幫你闖出出一條如何正確解決問題的道路、也讓你在遇到阻礙陷入困境無法前行的時候依然堅定執(zhí)著著望向遠(yuǎn)方。。。

架構(gòu)

架構(gòu)會考慮到源代碼的設(shè)計,文件排列,程序庫/模塊使用,他們交織在一起編織出了應(yīng)用程序。架構(gòu)形式會隨著應(yīng)用程序的開發(fā)不斷發(fā)生變化,它可能是一個web應(yīng)用服務(wù)器,像express這樣的處理靜態(tài)資源(比如圖片),也可能是一個scheduler/worker pipeline,排隊以及處理隊列中的項。

無論何種目的,都有一些通用的原則應(yīng)該遵守。

  • 模塊化。盡量保證你的代碼遵守DRY原則(Don't Repeat Yourself(不要重復(fù)自己))。如果你發(fā)現(xiàn)你需要在許多不同的地方使用一段相似的代碼,通常應(yīng)該將他們放在一個單獨(dú)的文件(或模塊)的一個函數(shù)中,從而形成通用幫助函數(shù)集模塊。這個模塊可以在其他依賴它的地方使用require()函數(shù)來加載。這樣做的目的不僅僅是防止多次重寫相似的功能,并且當(dāng)你升級功能時,你只需要修改同一個函數(shù)即可。
  • 遵守Node規(guī)范,盡量保證第三方node模塊放在node_modules文件夾下。同時也應(yīng)該保證node_modules在你的.gitignore里,這樣你就不會提交不相干的依賴文件。
  • 分離關(guān)系。前端相關(guān)的內(nèi)容(靜態(tài)CSS,javascript,HTML,模板文件,圖片,資源文件)應(yīng)該與后端應(yīng)用邏輯(路由,服務(wù)器,中間件)分離。同樣的,應(yīng)該將部署腳本,配置文件,數(shù)據(jù)關(guān)系文件和測試文件分開放置。

部署

你應(yīng)用到生產(chǎn)環(huán)境的方法很大程度上依賴于你堆棧的性質(zhì)。以下是我們嘗試過的一些方法:

  • 手動通過SSH傳到服務(wù)器和拷貝git倉庫。優(yōu)點:全手動控制,零部署工具設(shè)置。缺點:大量服務(wù)器時難以實施。所有的事情都需要手動設(shè)置,因此你不會得到任何好處,像upstart / initrc supervisation或者沒有日志的運(yùn)行。
  • Capistrano.優(yōu)點:對于團(tuán)隊開發(fā)者來說是標(biāo)準(zhǔn)的流程,只需簡單的運(yùn)行:cap deploy。缺點:難以配置,需要Ruby依賴。
  • Chef 腳本. 優(yōu)點:通過腳本安裝程序。缺點:每次想部署時,需要重啟服務(wù)器。Chef最常被用于服務(wù)器的安裝/配置,而不是應(yīng)用程序部署。
  • Deliver. 當(dāng)我們厭倦了其他的一些操作時,我們可以使用GoSquared出品的這個部署工具。
    它的靈感來自于Heroku’s的git推送系統(tǒng)的基礎(chǔ)部署。你只需要為應(yīng)用程序配置一個系統(tǒng)用戶(那些你需要做的——我們都幫你自動做好了),建立一個基本的分發(fā)配置,在工程里運(yùn)行分發(fā)的命令行。它使用git通過SSH把應(yīng)用程序推送到你的服務(wù)器,可以使用 foreman或者equivalent來監(jiān)聽?wèi)?yīng)用程序的啟動,復(fù)位和恢復(fù)。

這并不是一個詳盡的部署方案的列表,你可能需要發(fā)揮一點創(chuàng)意來構(gòu)想一個最適合您自個需求的解決方案。無論你采用何種策略,將部署配置文件納入您的應(yīng)用程序的源代碼版本控制并將部署流程記錄在您的README文件中都是一個好主意。

配置
 

幾乎每個應(yīng)用程序都有一些常量和設(shè)置需要能被方便地更改。常見的有主機(jī)名,端口號,超時時間,模塊選項和錯誤。在一個地方保存這些值將非常有利,可存在一個文件或多個文件中,如果這些值足夠多。這樣做可以使他們能更快地被修改,而不必花時間在梳理代碼以跟蹤查找到它們。

我之前只是轉(zhuǎn)儲配置的設(shè)置到一個輸出配置屬性為對象的文件。這在一個非常特殊的環(huán)境下運(yùn)行良好,比如在生產(chǎn)環(huán)境的時候,但隨著時間的推移,這開始成為維護(hù)的一個瓶頸,變得沒有條理性,并且在應(yīng)用改變時產(chǎn)生了多個文件作為一個基礎(chǔ)設(shè)施。

我們可以通過環(huán)境配置獲得想要的結(jié)果,這個想法是指你可以在應(yīng)用程序運(yùn)行時修改基于環(huán)境的配置值。這個方式很簡單,輸出一個叫做$NODE_ENV 的shell環(huán)境變量包含一個你將要運(yùn)行程序的環(huán)境模式標(biāo)識。你的應(yīng)用程序?qū)⒍ㄖ婆渲玫脑O(shè)置,當(dāng)它啟動時使用你定義好的環(huán)境。

環(huán)境方面的配置為你提供了貫穿整個應(yīng)用程序生命周期的更多的靈活性,你應(yīng)該可以在本地開發(fā)和運(yùn)行你的應(yīng)用程序,而不需要網(wǎng)絡(luò)連接(你想要在火車上能夠進(jìn)行?事實上寫這篇文章時我正好在火車上),這需要為本地服務(wù)指定主機(jī)地址和端口號。然后,你可能會希望在部署到服務(wù)器之前能夠進(jìn)行測試。這些都需要不同的配置。

我們一般使用 node-config,一個為工作精確設(shè)計的模塊。所有你需要做的是在config/default.js中定義配置的值,接下來為你不同的環(huán)境建一個文件,包含繼承了default.js的默認(rèn)指令。你設(shè)置$NODE_ENV作為環(huán)境變量的名字,這個模塊會覆蓋defaults.js中定義的[$NODE_ENV]屬性。導(dǎo)入到你的應(yīng)用程序合并配置對象,你只需使用require()就可以。

日志,度量和監(jiān)測

你想給自己足夠的應(yīng)用的不正常行為的證據(jù),以至于你就可以在盡可能很少的時間內(nèi)使‘b0rked’變?yōu)?rdquo;所有錯誤已經(jīng)修正“。一個最好的辦法是依賴日志。通常的前提是,如果得到了錯誤,記錄它。你必須遵從node的錯誤處理規(guī)則,在Callback的第一個章節(jié)已經(jīng)預(yù)設(shè)了當(dāng)一個錯誤發(fā)生時的錯誤信息:

makeRyanDahlProud(function(err, result){
  if(err){
    console.log(err);
  }
});

不管怎樣,如何應(yīng)對錯誤完全在于你。你或許想記錄錯誤,然后繼續(xù);蛘吣阆虢K止執(zhí)行這個回調(diào)。無論如何,你應(yīng)該在未來應(yīng)用這些錯誤,而且記錄日志是最簡單的實現(xiàn)方式。

盡管記錄錯誤日志是一個很好的方法,但是也會導(dǎo)致大量的消息被發(fā)送到logs日志或者終端。TJ Holowaychuck開發(fā)了一個叫做 debug的模塊,允許為日志消息創(chuàng)建命名空間,通過這途徑,我們就可以過濾掉不必要的消息,而通過命名空間抓取我們需要的消息。TJ的 repertoire還有大量其他模塊。

指標(biāo)

應(yīng)用指標(biāo)為我們提供了有用的信息,以便查看應(yīng)用在做什么操作和這些操作的時間間隔。它給我們提供一個重要的途徑去檢測異常的事件,瓶頸還有擴(kuò)充收縮計劃的參考。我將這些歸結(jié)到一個小的模塊,叫做abacus,它可以幫助我們維護(hù)計數(shù)器的集合,并通過statsd把它們以可視化繪圖形式描繪到graphite。實踐證明,保證應(yīng)用在給定的參數(shù)下運(yùn)行時非常方便。

監(jiān)測

監(jiān)測并非是必需的,但是監(jiān)測是一個很好的途徑,讓我們了解運(yùn)行應(yīng)用的服務(wù)器的資源使用率。另外的早期警告系統(tǒng),同樣非常重要,可以讓我們避免一些低級問題而導(dǎo)致的應(yīng)用崩潰。沒有比服務(wù)器硬盤空間不足而導(dǎo)致的應(yīng)用崩潰,或者CPU使用超過負(fù)載而崩潰,而更讓人窘迫。

現(xiàn)在有大量的監(jiān)測工具或者服務(wù):開源的有Ganglia, Monit, Sensu ,還有SaaS服務(wù)ServerDensity, NodeTime and NewRelic 。

容錯

在部署的時候也需要考慮的,就是你還需要考慮如果你的應(yīng)用崩潰后,將會發(fā)生什么。應(yīng)用最好在系統(tǒng)監(jiān)測程序下受控,例如Ubuntu的upstart。配置upstar十分瑣碎,但可以在應(yīng)用崩潰時,處理開始,結(jié)束,和重啟,所以還是值得的。Foreman有一個導(dǎo)出功能,可以為你的foreman-backed應(yīng)用生成upstar配置。

即使應(yīng)用可以在崩潰后重啟,但是這樣做的含義是什么?這樣會讓服務(wù)在某些時候中止么?會丟失數(shù)據(jù)么?會留下一些為完成的任務(wù)么?一串問題你需要在設(shè)計時考慮,冗余是一個很好的解決方案。例如,你需要在多臺服務(wù)器之間傳輸數(shù)據(jù),可以考慮增加一個反向代理(負(fù)載均衡,例如 HAProxy  或者web服務(wù)器,例如  nginx),從負(fù)載中移除應(yīng)用中的不完善實例,直到健康檢查達(dá)到合格分?jǐn)?shù)為止。

效率和可伸縮性

也許在開始階段我們很少考慮,但隨著你的應(yīng)用的成長,事務(wù)壓力會不斷增大,你也許需要考慮讓應(yīng)用變得更高效,甚至是可伸縮性。這里存在的風(fēng)險是過早的優(yōu)化。你不需要擔(dān)心讓你的應(yīng)用擁有超強(qiáng)的可伸縮性或者擁有超級性能,因為在真正面對這么高的負(fù)載的之前,你為什么為此而苦惱呢?你還是把最寶貴的時間放在核心組件的構(gòu)建上,或者"最小的可行性",就如,剛開始啟動,至少先到達(dá)需要擴(kuò)展的階段。

當(dāng)你的應(yīng)用在單節(jié)點和單實例的情況已不能滿足需求的時候,你有幾個選擇,所有的權(quán)衡,就像在雷區(qū)中擺布一樣。首先要做的是,找出瓶頸。為什么應(yīng)用會那么慢?是不是CPU占用率已達(dá)到頂峰值?是不是硬盤I/O不夠大或者是否足夠的連貫?

有時候最直接的答案是把應(yīng)用遷移到擁有足夠資源的服務(wù)器上。如果這對于你來說,是可行的,那么任務(wù)就比在多節(jié)點上重新架構(gòu)你的應(yīng)用要簡單的多,但是如果應(yīng)用增長速度很快,那這個可不是最好的解決方案。但是這個方法可能會消耗你大量的時間去構(gòu)建。

在多個服務(wù)器上橫向擴(kuò)展應(yīng)用,是一個棘手的方案,并且引入大量的錯誤,但是長期來說卻帶來生命力和令人著迷的技術(shù)挑戰(zhàn)。

文檔和組內(nèi)協(xié)作

一個應(yīng)用如果沒有文檔,就像一個包裝盒沒有說明書一樣。你可以找出需要做什么,但這樣做是笨拙的,浪費(fèi)時間和不精確的。最好你能提供清晰的文檔描述,簡要說明你的代碼。這樣做不單單是幫助其他人很快的上手和快速的開發(fā),而且還能幫助你在六個月后重新拾起這個應(yīng)用,修復(fù)臭蟲。

我并不主張像寫小說或者教程一樣去描述你的代碼。很多代碼可以自解釋,只要代碼足夠的簡潔。相反,你的文檔需要描述的是,代碼未能解釋的灰色地帶。注釋可以描述代碼的功能性,還有描述設(shè)計的理念,平衡,依賴,陷阱,邊緣例子和其他需要考慮的東西。隨著應(yīng)用的成長,代碼的描述文檔也會增長,以反映應(yīng)用變得越來越穩(wěn)定的過程。在應(yīng)用還處于初級階段的時候,沒必要太投入去編寫文檔。因為文檔在初期會經(jīng)常改變,當(dāng)你花了很多時間在它身上的時候,可能會很快又被廢除掉。

每個應(yīng)用程序都應(yīng)該包括一個README[MD],其中包含所有需要知道是應(yīng)用程序正常工作的細(xì)節(jié)。通常這包括:

  • 應(yīng)用程序其目的的簡短說明
  • 安裝說明
  • 啟動說明
  • 測試說明
  • 如何部署
  • 其他需要知道的點

我們需要一個模塊提前代碼里的注釋并生成干凈,有吸引力的文檔。我們會使用一個叫 docker  的模塊在我們的app中。

測試

以前我壓根沒有認(rèn)識到測試有多么重要,也沒有被(QA)煩過?赡苁且驗槲遥ㄌA耍⿵膩頉]有遇到這樣悲催的情況:多年以后,之前開發(fā)的應(yīng)用開始出問題了,你無法保證哪部分還正常工作,哪部分就快癱了?墒乾F(xiàn)在,經(jīng)歷過這些狗血的事情后。我知道,我們需要測試,我們需要偉大的QA。!

嚴(yán)格的測試可以造就好的應(yīng)用。你需要考慮橫向地把應(yīng)用切分為組件,而讓組件可以單獨(dú)的測試。這超越了基本的單元測試的迂腐,包含更多信息的組件和集成測試,可以讓你找到一個方法,以保證你的應(yīng)用可以很好的集成在一起工作。

最好的方式是在你開發(fā)應(yīng)用的時候編寫測試用例(一旦你有足夠的信心,你測試的功能不再有變化),那樣隨著應(yīng)用的開發(fā)進(jìn)程,測試也可以得到足夠的保證。這樣可以保證在開發(fā)新的功能的時候不會打斷現(xiàn)有的功能。測試同樣給其他開發(fā)者提供一個很好的例子,理解應(yīng)用各個組件如何工作和結(jié)果是什么。

現(xiàn)在有大量的測試框架可供選擇,不同的技術(shù)( BDD,  TDD).  我們嘗試過 vows 和 tap,但是我目前更喜歡用 mocha和should.js,因為我覺得使用它們,比使用純javascript,更能平衡架構(gòu)和工具,同時允許使用同樣的代碼庫,腳本文件,而且還能在你的測試用例中啟動服務(wù)器。

依賴
 

Node擁有一個強(qiáng)大的模塊系統(tǒng)和被稱為npm的包管理工具,npm能幫助你無縫的集成各種模塊到你的應(yīng)用中。npm給你提供了一個非常豐富的開源模塊的目錄 索引,你可以找到你想要的。

一旦你知道你的應(yīng)用需要什么組件(例如和redis數(shù)據(jù)庫溝通的是什么客戶端),明智的選擇是首先檢查現(xiàn)有的配置索引中的用戶級節(jié)點模塊。Node.js是黑客們中最喜歡的實驗工具,所以這里有很多模塊可供你嘗試。自然,這些組件的質(zhì)量都不一樣,所以你需要了解哪些是好的模塊,哪些卻不是(名單:README, 測試,例子,作者),但是基本上可以提供給你所有你需要的。

英文原文:https://engineering.gosquared.com/10-vital-aspects-of-building-a-node-js-application

 

關(guān)鍵詞:Nodejs

贊助商鏈接: