微服務介紹
這是一篇由 Chris Richardson 撰寫的客座文章。Chris 是 CloudFoundry.com 的創始人,這是一個早期的用于 Amazon EC2 的 Java PaaS (Platform-as-a-Service平臺即服務)。他現在為大型組織提供咨詢服務,幫助他們提升開發和部署應用的能力。他還在有規律地寫關于微服務的博客,博客地址:http://microservices.io。
==============
目前,微服務得到較多的關注:論文,博文,社交媒體上的討論,還有會議報告。他們處于期望膨脹期的頂峰,快速地向著登上 Gartner 趨勢報告前進。同時,在軟件社區還有一群懷疑論者,他們無視微服務,認為它沒什么新意。反對派們聲稱,這種想法就是 SOA 的馬甲。但是,不管是大肆宣傳還是懷疑主義,微服務架構模式具有明顯的好處——尤其談到敏捷開發和復雜企業應用交付的時候。
這篇博文是一個關于設計、構建和部署微服務的七部系列的第一部分。你將會了解到微服務的方法和它與傳統的單一架構模式的對比。這個系列將會詳細說明微服務架構的眾多元素。你將會了解到微服務架構模式的好處和缺點,對你的項目是否有意義,和怎樣應用它。
現在,我們先看看為什么你應該考慮使用微服務。
構建整體應用
假設你想要構建一個全新的打車應用與 Uber 和 Hailo 競爭。經過了一些預備會議和需求收集之后,你將會手工創建一個新的工程,或者使用 Rails、Spring Boot、Play 或 Maven 這類工具生成一個。這個新應用將會有一個模塊化的六角形架構,就像下圖這樣:
該應用的核心是服務模塊、域對象模塊和事件模塊實現的業務邏輯。圍繞在核心周圍的是與外界交互的適配器。這些適配器包括數據庫訪問組件、生產和消費消息的消息傳遞組件、暴露 API 或實現 UI 的 web 組件。
盡管具有合乎邏輯的模塊化架構,但是該應用做為整體打包部署。具體的樣式依賴應用的語言和框架。例如許多Java應用打成WAR包,部署在 Tomcat或Jetty這樣的應用服務器。還有一些Java應用打成獨立可執行的JAR包。類似的,Rails和Node.js應用以目錄層次的形式打 包。
寫成這種形式的應用極其普遍。它們非常易于開發,因為我們的IDE和其他工具專注于構建一個單獨的應用。這樣的應用通常也易于測試。通 過簡單地運行應用就可以實現端到端的測試,使用Selenium就可以測試UI。整體應用也易于部署。你僅僅需要復制打包的應用到服務器。你也可以通過在 負載均衡器后面運行多個副本的方式擴展應用。在項目的早期階段,這種方式運作的很好。
走向整體地獄(Monolithic Hell)
很不幸,這種簡單的方式有一個巨大的限制。成功的應用都會隨著時間增大,最終變得巨大。在每一個沖刺(sprint),你的開發團隊實現了許多用戶故事,這當然意味著添加了許多行代碼。經過幾年之后,原本很小、很簡單的應用將會長成可怕的龐然大物。舉一個極端的例子,我最近與一個開發者進行了一次談話,他寫了一個工具分析他們數百萬行代碼的應用中的成千上萬個JAR包的依賴。我很確定這樣一個怪獸是由大量的開發者經過數年的共同努力才制造出來的。
一旦你的應用變成了一個復雜的龐然大物,你的開發團隊就可能生活在痛苦之中。敏捷開發的任何嘗試和交付將會舉步維艱。該應用的一個主要問題是過度地復雜。 它僅僅是太大了以至于任何一個開發者都不能完整地理解。因此正確地修復漏洞和實現新功能將會變得困難和耗時。更糟糕的是,這形成了一個惡性循環。如果基本 代碼難于理解,就很難做出正確的改動。那么最終就會成為一個可怕的、無法理解的大泥球。
應用程序的規模也會拖慢開發進度。應用程序的規模越大,啟動的時間越長。例如在最近的調查中,開發者表示啟動時間長達12分鐘。我也道聽途說過啟動時間長達40分鐘的應用程序。如果開發者不得不周期性重啟應用服務,那么他們一天中很大一部分時間將浪費在等待上,他們的生產力將會受到影響。
巨大的、復雜的整體應用程序的另外一個問題是它是持續部署的障礙。目前,SaaS應用通常在一天之內會多次將改動推到生成環境。對于復雜的龐然大物,這極 其難處理,因為你必須重新部署整個應用來更新程序的任何一小部分。我前面提到的冗長的啟動時間也將產生不利的影響。而且因為改動的影響通常不能被充分的理 解,那么你可能還必須做更廣泛的手工測試。最終導致持續部署幾乎是不可能的。
當不同的模塊具有資源需求沖突的時候,整體應用程序也將難以擴展。例如,某個實現CPU密集型圖像處理邏輯的模塊非常適合部署在AWS EC2 Compute Optimized instances。另外某個內存數據庫的模塊最適合部署在 EC2 Memory-optimized instances。然后由于這些模塊都得部署在一起,所以你不得不在硬件的選擇上做出妥協。
整體應用程序的另外一個問題是可靠性。因為所有的模塊都在同一進程內運行,所以任一模塊的漏洞,比如內存泄露,將會影響整個進程。此外,因為應用程序的所有實例是一致的,所以這些漏洞將會影響整個應用的可用性。.
最后但并非不重要,整體應用程序使得采用新框架、新語言極其困難。舉個例子來說,假設你有兩百萬行使用XYZ框架寫的代碼。那么使用新的ABC框架重寫整 個應用將會是極其昂貴的(無論是時間還是花費),即使新框架相當的好。因此對于采用新技術,整體應用程序具有巨大的障礙。當你開始新的項目的時候,你將非 常糾結于選擇哪種技術。
總而言之:你有一個已經長成可怕的龐然大物的,幾乎沒有開發者可以理解的,成功的業務關鍵應用。這個應用程序使用過時的、沒有生產力的技術寫成,這使得招聘優秀的開發者變得非常困難。這個應用程序難以擴展而且不可靠。因此,敏捷開發和應用交付是不可能的。
所以你能怎么辦呢?
微服務 – 處理復雜性
許多組織,比如Amazon、eBay和Netflix,已經采用現在被稱為微服務架構模式的方法解決了這個問題。其想法是將應用程序分割成更小的相互關聯的服務,而不是構建單個可怕的整體應用程序。
一個服務通常實現一組獨立的特性或功能,比如訂單管理、客戶管理等。每個微服務是一個具有六角形架構的迷你應用,其自己的六角形架構包含業務邏輯以及許多 適配器。某些微服務將會暴露供其他微服務或者客戶端使用的API。另外一些微服務可能實現web UI。在運行時,每個實例通常是一個云虛擬機或者一個Docker容器。
例如,前文所述的系統的一個可能的分解如下圖所示:
應用的每個功能區域現在以微服務的方式實現。此外web應用程序分割成了一組更簡單的web應用程序(一個面向乘客的,一個面向司機的)。這使得更容易為特定的用戶、設備或特定的用例部署單獨的服務。
每個后端服務暴露一套 REST API,大部分服務調用其他服務提供的 API。例如司機管理模塊使用通知服務告知空閑的司機可能的訂單。UI 服務調用其他服務來渲染 web 頁面。服務也可能使用異步的、基于消息的通信方式。服務間通信將會在本系列后面的文章中詳細討論。
某些 REST API 是暴露給司機和乘客使用的移動 app 的。然后 app 不能直接訪問后端服務,其間的通信是通過稱為 API 網關的媒介傳遞的。API 網關負責負載均衡、緩存、訪問控制、API 測量和監控,該模塊可以使用 NGINX 有效的實現。本系列后面的文章將會討論 API 網關。
微服務架構模式對應擴展立方體(Scale Cube)的 Y 軸擴展,擴展立方體是《The Art of Scalability》一書中描述可擴展性的 3D 模型。此外還有兩個擴展維度,X 軸擴展表示在負載均衡器后面運行多個相同的應用程序副本,Z 軸擴展(數據分割)表示使用請求中的某個屬性(例如數據表主鍵或用戶 id)來路由請求到特定服務器。
應 用通常綜合使用三種擴展。Y 軸擴展分解應用程序到微服務,就像上圖展示的那樣。在運行時,為了吞吐量和可用性,X 軸擴展在負載均衡器后面運行多個服務的實例。某些應用也可能使用 Z 軸擴展分割服務。下圖展示了如何使用 Docker 將訂單管理服務部署在 AWS EC2 上。
在運行時,訂單管理服務包含多個服務實例。每個服務實例是一個 Docker 容器。為了高可用性,這些容器運行在多個云虛擬機上。在這些服務實例前面是一個諸如 NGINX 這樣的負載均衡器在多個實例之間分發請求。負載均衡器也可能處理一些別的事情,比如緩存、訪問控制、API 測量和監控。
微 服務架構模式顯著地影響了應用程序與數據庫之間的關系。每個服務有自己的數據庫模式,而不是共享單個數據庫模式。盡管這種方式與企業級數據模型的想法相 悖,也會造成某些數據的冗余。但是如果你想獲得微服務的好處,那么每個服務一個數據庫模式是很關鍵的,因為這確保了松散耦合。下圖展示了應用的數據庫架 構。
每個服務有其自己的數據庫。那么服務就可以選擇使用最符合需求的數據庫,這就是所謂的混合持久化架構。例如查找乘客附近司機的司機管理模塊必須使用支持高效地理查詢的數據庫。
表 面上微服務架構模式類似于 SOA。這兩種架構都包含一組服務。你可以認為微服務架構模式就是不包括商業化和 Web 服務規范(WS-)、企業服務總線(ESB)的 SOA。基于微服務的應用傾向于使用更簡單輕量級的協議,比如 REST 而不是 WS-。很大程度上也避免使用 ESB,取而代之的是使用微服務自己實現類似 ESB 的功能。微服務架構模式也拒絕 SOA 的其他部分,比如規范模式的概念。
微服務的好處
微服務架構模式有許多重要的好處。第一,它解決了復雜性的問題。它將一個可怕的、龐大的整體應用分解成一組服務。在 整體的功能沒有改變的同時,應用程序已經被分解成可管理的模塊或服務。每個服務有以 RPC 或者消息驅動 API 形式定義清楚的界限。微服務架構模式加強了一定程度的模塊化,這在整體應用程序中是很難實現的。因此單個的服務可以更快的開發,更簡單的理解和維護。
第 二,這種架構使得每個服務可以由單獨的團隊獨立開發,這些團隊可以專注于某個服務。開發者可以自由地選擇合理的技術,只要服務遵守 API 約定即可。當然大部分組織想要避免混亂地完全無限制的技術選項。然后這種自由意味著開發者不在受限于使用可能過時的技術開始新的項目。當開始寫一個新服務 的時候,他們可以選擇使用當前的技術。而且因為服務相對較小,所以使用當前的技術重寫老服務是可行的。
第三,微服務架構模式使得每一個微服務能被獨立部署。開發者再也不需要調整本地對其服務的更變而進行部署。各種類型的變更能在他們測試時立即部署。 UI 團隊也可以這樣做,舉例來說,當 UI 發生改變時,能執行 A|B 測試并快速迭代。微服務架構模式讓持續部署成為可能。
最后,微 服務架構模式使得每一個服務都可以被獨立擴展。你可以部署大量恰好符合要求容量和有效約束條件的服務實例。此外,你可以使用最匹配服務資源要求的硬件。例 如,你可以在計算優化過的 EC2上部署一個密集CPU 鏡像處理服務實例,還可以在內存優化的 EC2 上部署內存數據庫服務實例。
微服務的缺點
就像 Fred Brooks 在30年前說的,沒有銀彈。跟別的技術一樣,微服務架構也有缺點。其中的一個缺點就是名字本身。微服務這個詞過分強調服務的規模。實際上有些開發者支持構 建極其細粒度10-100 LOC 的服務。盡管規模小的服務更可取,但是最好記住這只是手段而不是目的。微服務的目的是充分地分解應用程序以促進敏捷開發和部署。
微服務另外 一個主要的缺點是微服務應用做為分布式系統帶來的復雜性。開發者需要選擇或者實現基于消息或 RPC 的進程間通信機制。而且必須編寫處理部分失敗的代碼,因為請求的目的地可能很慢或者不可用。雖然這都不是高深莫測的事情,但是相對于整體應用程序這明顯更 復雜,因為整體應用程序中模塊間的調用是通過語言層面的方法/程序調用實現的。
微服務的另外一個挑戰是分割的數據庫架構。更新多個業務實體的事務相當普遍。這種事務在整體應用程序中很容易實現,因為只有一個數據庫。然后在基于微服務的應用中,你需要更新多個屬于不同服務的數據庫。分布式事務通常不是最好的選擇,不僅僅因為 CAP 理論,而且目前許多高擴展性的 NoSQL 數據庫和消息代理就不支持。你最終不得不使用基于最終一致性的方法,這對于開發者來說更具挑戰性。
測 試微服務也很復雜。使用 Spring Boot 這樣的現代框架很容易開始一個整體 web 應用程序,編寫測試類測試其 REST API。于此相反,對于微服務的一個類似的測試則需要運行該服務以及依賴的服務(或者至少需要配置那些服務的存根)。這也不是高深莫測的事情,但是不要低 估做這些事情的復雜性。
微服務架構模式另外一個主要的挑戰是實現跨服務的需求變更。設想你實現的用戶故事需要更改服務 A,因為 A 依賴 B,B 依賴 C,所以你又得更改服務 B 和 C。在整體應用中,你可以簡單地更改對應的模塊,集成這些變更一起部署。相反在微服務架構模式中,你需要仔細計劃和協調各個服務的更改上線。例如你需要首 先更新服務 C,接著是服務 B,最后才是服務 A。很幸運的是大部分改動通常只影響一個服務,需要協調的跨服務更改相對較少。
部署一個基于微服務的應用也很復雜。整體應用程序簡單地部署在一組相同的服務器上,在這些服務器前面是一個傳統的負載均衡器。每個應用程序實例配置好基礎設施服務的位置(主機和端口),例如數據庫和消息代理。相反微服務應用通常包含大量服務。例如 Hailo 有160個不同的服務,Adrian Cockcroft 聲 稱 Netflix 有超過600個服務。每個服務有多個運行實例。有太多運行的實例需要配置、部署、擴展和監控。另外你也需要實現一個服務發現機制(這將會在后面的文章討 論)以確保某服務可以找到需要通信的其他服務的位置(主機和端口)。傳統的麻煩的基于票據的手工配置方式無法處理這個層次的復雜性。因此成功的部署微服務 應用需要開發者對部署方法有更強的控制,已經更高水平的自動化。
自動化的一個方法是使用現成的 PaaS,比如 Cloud Foundry。PaaS 為開發者提供了一種簡單的方式部署和管理微服務。PaaS 可以使開發者不必考慮采購和配置 IT 資源的問題。同時,配置 PaaS 的系統和網絡專家可以確保符合最佳實踐滿足公司的需求。另外一種自動化微服務部署的方式本質上就是開發自己的 PaaS。這通常是從使用某個集群解決方案開始的,比如結合 Docker 這類技術的 Mesos 或Kubernetes。在本系列的后續文章中我們將會看到基于軟件的應用交付方法如何解決這個問題,比如NGINX,可以很容易地處理微服務層面的緩存、訪問控制、API 測量和監控。
總結
構建復雜的應用本身就很難。整體架構只適合簡單的輕量級應用。對于復雜的應用,如果你選用整體架構,那么最終你會生活在痛苦之中。微服務架構對于復雜的、演化的應用是更好的選擇,盡管微服務架構有些缺點和實現上的挑戰。
在之后的文章中,我將會深入微服務架構模式的各個方面,探討服務發現、服務部署、整體應用程序重構策略這些主題。
請繼續關注…