背景
在開發(fā)服務(wù)端企業(yè)應(yīng)用時(shí),應(yīng)用需要支持各種不同類型的客戶端,比如桌面瀏覽器、移動瀏覽器以及原生移動應(yīng)用。應(yīng)用還需要向第三方提供可訪問的API,并通過Web Service或者消息代理與其它應(yīng)用實(shí)現(xiàn)集成。應(yīng)用通過執(zhí)行業(yè)務(wù)邏輯、訪問數(shù)據(jù)庫、與其它系統(tǒng)交換信息、并返回一條HTML/JSON/XML響應(yīng),來處理請求(HTTP請求與消息)。
應(yīng)用采用多層架構(gòu)或者六角架構(gòu),主要由以下幾類不同組件構(gòu)成:
- 展現(xiàn)組件——負(fù)責(zé)處理HTTP請求并響應(yīng)HTML或者JSON/XML(對于web Services APIs)
- 業(yè)務(wù)邏輯——應(yīng)用的業(yè)務(wù)邏輯
- 數(shù)據(jù)庫訪問邏輯——用于訪問數(shù)據(jù)庫的數(shù)據(jù)訪問對象
- 應(yīng)用集成邏輯——消息層,例如基于Spring Integration
不同邏輯組件分別響應(yīng)應(yīng)用中的不同功能模塊。
問題
應(yīng)用的部署架構(gòu)是什么?
需求
- 應(yīng)用需要由一個(gè)開發(fā)者團(tuán)隊(duì)專門負(fù)責(zé)
- 團(tuán)隊(duì)新成員需要快速上手
- 應(yīng)用應(yīng)該易于理解和修改
- 對應(yīng)用能夠進(jìn)行持續(xù)部署
- 需要在多臺設(shè)備上運(yùn)行應(yīng)用副本,從而滿足可擴(kuò)展性與可用性的要求
- 使用各種新技術(shù)(框架、編程語言等)
方案
使用單體架構(gòu)構(gòu)建應(yīng)用。例如:
- 單個(gè)Java WAR文件。
- 單個(gè)Rails或者NodeJS代碼目錄層級。
舉例
假設(shè)需要構(gòu)建一款電子商務(wù)應(yīng)用程序,使其能夠接收來自客戶的訂單、驗(yàn)證庫存信息與可用信用額度,而后進(jìn)行發(fā)貨。該應(yīng)用程序會包含多個(gè)組件,其中StoreFrontUI負(fù)責(zé)實(shí)現(xiàn)用戶界面,而其它后端服務(wù)則分別負(fù)責(zé)檢查信用額度、維護(hù)庫存信息以及發(fā)送訂單。
應(yīng)用被當(dāng)作一個(gè)單體進(jìn)行部署。例如:一個(gè)Java Web應(yīng)用僅包含一個(gè)運(yùn)行在Tomcat之類的Web容器上WAR文件。一個(gè)Rails應(yīng)用由單一目錄層級構(gòu)成,該目錄層級的部署通過在Apache/Nginx上使用Phusion Passenger,或者在Tomcat上使用JRuby得以實(shí)現(xiàn)。為了提高擴(kuò)展性和可用性,你可以在負(fù)載均衡器之后運(yùn)行此應(yīng)用的多個(gè)實(shí)例。
?
結(jié)果
這類解決方案擁有以下優(yōu)勢:
- 易于開發(fā)——當(dāng)前開發(fā)工具與IDE的設(shè)計(jì)目標(biāo)即在于支持單體應(yīng)用的開發(fā)。
- 易于部署——你只需要將該WAR(或者目錄層級)部署在合適的運(yùn)行環(huán)境中即可。
- 易于擴(kuò)展——你可以在負(fù)載均衡器后面運(yùn)行多個(gè)應(yīng)用副本實(shí)現(xiàn)擴(kuò)展。
然而,一旦應(yīng)用變大、團(tuán)隊(duì)擴(kuò)大,這種方案的弊端將會變得愈發(fā)明顯:
- 單體應(yīng)用巨大的代碼庫可能會讓人望而生畏,特別是對那些團(tuán)隊(duì)新成員來說。應(yīng)用難以被理解和進(jìn)行修改,進(jìn)而導(dǎo)致開發(fā)速度減慢。由于沒有清晰的模塊邊界,模塊化會逐漸消失。另外,由于難以正確把握代碼變化,導(dǎo)致代碼質(zhì)量逐步下滑,陷入惡性循環(huán)。
- 過載的IDE——代碼庫越大,IDE速度越慢,開發(fā)者的生產(chǎn)效率越低。
- 過載的Web容器——應(yīng)用越大,Web容器啟動時(shí)間越長。容器啟動耗費(fèi)時(shí)間,極大影響到開發(fā)者的生產(chǎn)效率。對部署工作也有負(fù)面影響。
- 持續(xù)部署困難——巨大的單體應(yīng)用本身就是頻繁部署的一大障礙。為了更新一個(gè)組件,你必須重新部署整個(gè)應(yīng)用。這會中斷那些可能與更改無關(guān)的后臺任務(wù)(例如Java應(yīng)用中的Quartz任務(wù)),同時(shí)可能引發(fā)問題。另外,未被更新的組件有可能無法正常啟動。重新部署會增加風(fēng)險(xiǎn),進(jìn)而阻礙頻繁更新。因?yàn)橛脩艚缑骈_發(fā)者經(jīng)常需要進(jìn)行快速迭代與頻繁重新部署,所以這對用戶界面開發(fā)者而言更加是個(gè)難題。
- 應(yīng)用擴(kuò)展困難——單體架構(gòu)只能進(jìn)行一維伸縮。一方面,它可以通過運(yùn)行多個(gè)應(yīng)用副本來增加業(yè)務(wù)容量,實(shí)現(xiàn)擴(kuò)展。一些云服務(wù)甚至可以根據(jù)負(fù)載量動態(tài)調(diào)整實(shí)例數(shù)量。但在另一方面,數(shù)據(jù)量增大會使得該架構(gòu)無法伸縮。每個(gè)應(yīng)用實(shí)例需要訪問所有數(shù)據(jù),導(dǎo)致緩存低效,加大內(nèi)存占用和I/O流量。另外,不同的應(yīng)用組件有不同的資源需求——有的是CPU密集型的,另外一些是內(nèi)存密集型的。單體架構(gòu)無法單獨(dú)伸縮每個(gè)組件。
- 難于進(jìn)行規(guī)?;_發(fā)——單體應(yīng)用是規(guī)?;_發(fā)的障礙。應(yīng)用一旦達(dá)到特定規(guī)模,需要將現(xiàn)有組織拆分成多個(gè)團(tuán)隊(duì),每個(gè)團(tuán)隊(duì)負(fù)責(zé)不同的功能模塊。舉例來說,我們可能需要設(shè)立UI團(tuán)隊(duì)、會計(jì)團(tuán)隊(duì)、庫存團(tuán)隊(duì)等等。單體應(yīng)用的問題在于它使團(tuán)隊(duì)無法獨(dú)立展開工作。團(tuán)隊(duì)需要在工作進(jìn)度和重新部署上進(jìn)行協(xié)調(diào)。對于各團(tuán)隊(duì)而言,這使得變更和更新產(chǎn)品變得異常困難。
- 需要長期關(guān)注同一套技術(shù)?!獑误w架構(gòu)迫使我們長期使用在開發(fā)初期選定的技術(shù)堆棧(在某些情況下,可能是某些技術(shù)的特定版本)。單體應(yīng)用是漸進(jìn)采用新技術(shù)的障礙。舉例來說,如果我們選擇了JVM,那么我們可以選擇Java以外的一些語言,因?yàn)镚roovy和Scala等基于JVM的語言也可以和Java進(jìn)行良好的互操作。但此時(shí)以非JVM語言編寫的組件就無法在該單體架構(gòu)中使用。另外,如果大家所使用的應(yīng)用平臺框架已經(jīng)過時(shí),那么我們將很難將應(yīng)用遷移到其它更新并且更完善的框架當(dāng)中。有時(shí)候?yàn)榱瞬捎靡惶仔滦推脚_框架,我們甚至需要重寫整個(gè)應(yīng)用,這是風(fēng)險(xiǎn)很大的工作。
相關(guān)模式
微服務(wù)架構(gòu)是一種能夠解決單體架構(gòu)各種局限的備選模式。
已知案例
知名的互聯(lián)網(wǎng)服務(wù)商最初皆采用單體架構(gòu),包括Netflix、Amazon.com以及eBay等等。作者開發(fā)的大多數(shù)Web應(yīng)用也是用單體架構(gòu)。
原文鏈接?
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點(diǎn)擊舉報(bào)。