單體應用與微服務優缺點辨析
近日,Java Code Geeks發表了一篇文章,分析了單體應用與微服務的優缺點,並建議使用微服務重構現有的應用程式。
通俗地講,“單體應用(monolith application)”就是將應用程式的所有功能都打包成一個獨立的單元,可以是JAR、WAR、EAR或其它歸檔格式。單體應用有如下優點:
- 為人所熟知:現有的大部分工具、應用伺服器、框架和指令碼都是這種應用程式;
- IDE友好:像NetBeans、Eclipse、IntelliJ這些開發環境都是針對開發、部署、除錯這樣的單個應用而設計的;
- 便於共享:單個歸檔檔案包含所有功能,便於在團隊之間以及不同的部署階段之間共享;
- 易於測試
- 容易部署:只需將單個歸檔檔案複製到單個目錄下。
目前為止,單體應用已經很好地服務了我們,未來無疑還會繼續發揮重要作用。但是,不管如何模組化,單體應用最終都會因為團隊壯大、成員變動、應用範圍擴充套件等出現問題。下面是單體應用的一些不足:
- 不夠靈活:對應用程式做任何細微的修改都需要將整個應用程式重新構建、重新部署。開發人員需要等到整個應用程式部署完成後才能看到變化。如果多個開發人員共同開發一個應用程式,那麼還要等待其他開發人員完成了各自的開發。這降低了團隊的靈活性和功能交付頻率;
- 妨礙持續交付:單體應用可能會比較大,構建和部署時間也相應地比較長,不利於頻繁部署,阻礙持續交付。在移動應用開發中,這個問題會顯得尤為嚴重;
- 受技術棧限制:對於這類應用,技術是在開發之前經過慎重評估後選定的,每個團隊成員都必須使用相同的開發語言、持久化儲存及訊息系統,而且要使用類似的工具,無法根據具體的場景做出其它選擇;
- 技術債務:“不壞不修(Not broken,don’t fix)”,這在軟體開發中非常常見,單體應用尤其如此。系統設計或寫好的程式碼難以修改,因為應用程式的其它部分可能會以意料之外的方式使用它。隨著時間推移、人員更迭,這必然會增加應用程式的技術債務。
而隨著業務需求的快速發展變化,敏捷性、靈活性和可擴充套件性需求不斷增長,迫切需要一種更加快速高效的軟體交付方式。微服務就是一種可以滿足這種需求的軟體架構風格。單體應用被分解成多個更小的服務,每個服務有自己的歸檔檔案,單獨部署,然後共同組成一個應用程式。這裡的“微”不是針對程式碼行數而言,而是說服務的範圍限定到單個功能。微服務有如下特點:
- 領域驅動設計:應用程式功能分解可以通過Eric Evans在《領域驅動設計》中明確定義的規則實現;每個團隊負責與一個領域或業務功能相關的全部開發;團隊擁有全系列的開發人員,具備使用者介面、業務邏輯和持久化儲存等方面的開發技能;
- 單一職責原則:每個服務應該負責該功能的一個單獨的部分,這是SOLID原則之一;
- 明確釋出介面:每個服務都會發佈一個定義明確的介面,而且保持不變;服務消費者只關心介面,而對於被消費的服務沒有任何執行依賴;
- 獨立部署、升級、擴充套件和替換:每個服務都可以單獨部署及重新部署而不影響整個系統。這使得服務很容易升級,每個服務都可以沿著《Art of Scalability》一書定義的X軸和Z軸進行擴充套件;
- 可以異構/採用多種語言:每個服務的實現細節都與其它服務無關,這使得服務之間能夠解耦,團隊可以針對每個服務選擇最合適的開發語言、持久化儲存、工具和方法;
- 輕量級通訊:服務通訊使用輕量級的通訊協議,例如,同步的REST,非同步的AMQP、STOMP、MQTT等。
相應地,微服務具有如下優點:
- 易於開發、理解和維護;
- 比單體應用啟動快;
- 區域性修改很容易部署,有利於持續整合和持續交付;
- 故障隔離,一個服務出現問題不會影響整個應用;
- 不會受限於任何技術棧。
微服務看上去像一枚銀彈,可以解決許多軟體開發方面的問題。這看上去很美好,但並不易於實現。微服務會極大地增加運維工作量,InfoWorld在一篇文章中明確指出:
使用微服務,一些技術債務勢必從開發轉到運維,因此,你最好有一個一流的開發運維團隊。
因此,微服務對基礎設施提出了一些額外的需求。通常,我們將它們總稱為NoOps,本質上講,就是一組服務,提供一個更好的應用程式部署流程並確保其執行,包括服務複製、服務發現、服務恢復和服務監控。
不過,即使具備了上述條件,也並不是說就要拋棄現有的應用程式,在大多數情況下,我們無法做到。因此,我們要構建一種方法,依據它使用微服務重構現有的應用程式。雖然重構過程並不簡單,但長遠來看,重構一個單體應用可以一次性償還所有的技術債務。
Netflix是使用微服務的一個典型代表,它發表了數篇文章(1、2、3、4、5、6)分享採用微服務的一些經驗,感興趣的讀者可以進一步閱讀。