場景描述
假設你正在開發一個大型服務端企業應用,有如下需求:
- 必須支援多種客戶端,包括:WEB 端瀏覽器、WAP 端瀏覽器以及原生移動 APP。
- 對外暴露公共 API 用於呼叫
- 處理 HTTP 請求,或者訊息,執行對應的業務邏輯。
- 訪問資料庫,快取或者持久化響應的資料
- 與其他系統進行通訊,交換所需的資訊
- 返回 HTTP 響應,指定好特定的序列化方式,例如 JSON、 XML 等等
- 根據業務邏輯與功能,設計並劃分出不同邏輯模組
這樣的一個應用,你會如何設計架構並部署呢?
考慮因素
- 這是一個團隊開發的專案,有一個獨立團隊負責
- 團隊成員會發生變化,新加入的成員必須快速上手專案
- 應用程式必須易於理解並修改
- 期望能實現應用的持續整合與部署
- 必須可以多例項部署應用程式,以滿足可伸縮性和可用性要求。
- 想用比較新的技術(框架、程式語言等)
解決方案
使用單體架構,例如:
- 一個 Java WAR 檔案啟動的程式
- 一個單目錄 Rails 或者 NodeJS 程式
舉例
假設現在正在設計一個電商應用,功能包括接收來自客戶的訂單(StoreFrontUI),驗證並維護庫存餘額(Inventory Service),驗證並維護使用者可用餘額(Accounting Service),下單成功併發貨(Shipping Service)。這個應用被設計成一個單體架構應用,例如:JavaWeb 應用程式由執行在Web容器(如 Tomcat )上的單個 WAR 檔案組成。Rails 應用程式由部署在 Nginx 或 Tomcat 上的 JRuby 或 Nginx 上的單一目錄層次結構組成。可以在負載均衡器後面部署多個例項,以擴充套件和提高可用性。
分析
這種解決方案的好處有:
- 開發簡單,當前的 IDE 基本都是按照開發單體應用程式開發的。
- 部署簡單,只要把一個檔案或者目錄部署到 Web 容器裡即可。
- 擴容簡單,通過在負載均衡器後面部署多個例項就能實現擴容。
但是,隨著產品不斷迭代,這個單體應用程式將會變得越來越大,團隊的規模也越來越大,這種單體設計就會有一些缺點,並且這些缺點會變得越來越嚴重:
- 單體應用程式碼在同一個程式碼庫,這個程式碼庫會越來越大,使開發人員感覺會很頭大,特別是那些剛加入團隊的開發人員。應用程式將很難理解和修改,因此,開發速度通常會被減緩。另外,由於沒有明確的模組邊界,程式碼內部的模組化會隨著時間的推移而越來越模糊。此外,由於很難理解如何正確實現更改,並且可能還需要相容老版本的錯誤,因此程式碼的質量會隨著時間的推移而下降,慢慢堆積成為屎山。
- IDE 的壓力會很大。程式碼庫越大,IDE 會更慢,IDE 一般為了智慧補全程式碼的功能,會對程式碼做索引並載入到記憶體中。臃腫的程式碼會拖慢 IDE,降低開發效率。
- Web 容器壓力變大。程式越臃腫,啟動時間會被拖長,導致程式碼除錯變慢,同時部署時間也會變長。
- 持續整合部署難度越來越大。為了更新一個元件,您必須重新部署整個應用程式。這會導致所有業務,不管是否有更新,都被影響或者中斷。同時,如果出現問題,回滾時間也會增長。因此,這限制了程式不能持續頻繁更新。
- 不能靈活擴充套件。不同業務模組可能壓力不同,以及壓力大的時間段可能也不同,但是每次擴容,都需要所有模組一塊擴容,造成了浪費。
- 故障擴散。如果有一個模組出了問題導致記憶體洩漏,那麼整個業務都會受到影響。
- 團隊分工的障礙。例如,我們可能希望有UI團隊、會計團隊、庫存團隊等等。單塊應用程式的問題在於它阻止了團隊獨立工作。小組必須協調他們的開發工作和重新部署。對於一個團隊來說,進行更改和更新生產要困難得多。
- 需要長期使用同一個技術棧。一種單一的體系結構迫使您與您在開發開始時所選擇的技術堆疊(在某些情況下,與該技術的特定版本)結合在一起。有了單體應用程式,就很難逐步採用一種較新的技術。比如你使用的框架停止更新,或者過時了,在單體應用下很難逐步採用一個新的框架實現。