淺談微服務

oschina發表於2015-05-23

目前,微服務得到較多的關注:論文,博文,社交媒體上的討論,還有會議報告。他們處於期望膨脹期的頂峰,快速地向著登上 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 測量和監控。

總結

構建複雜的應用本身就很難。整體架構只適合簡單的輕量級應用。對於複雜的應用,如果你選用整體架構,那麼最終你會生活在痛苦之中。微服務架構對於複雜的、演化的應用是更好的選擇,儘管微服務架構有些缺點和實現上的挑戰。

在之後的文章中,我將會深入微服務架構模式的各個方面,探討服務發現、服務部署、整體應用程式重構策略這些主題。

請繼續關注…

相關文章