
作者:Chris Richardson 和 Floyd Smith
原文:
Introduction to Microservices
如今微服務倍受關注:文章、部落格、社交媒體和會議演講都在讨論微服務。微服務正在迅速朝着
加德納技術成熟度曲線(Gartner Hype cycle)的高峰前進。與此同時,也有持懷疑态度的軟體社群人員認為微服務沒什麼新鮮可言。反對者聲稱它的思想隻是面向服務架構(SOA)的重塑。然而,無論是炒作還是懷疑,不可否認,
微服務架構模式有非常明顯的優勢 —— 特别是在實施靈活開發和複雜的企業應用傳遞方面。
本書的七個章節主要介紹如何設計、建構和部署微服務,這是本書的第一章。在此章節中,你将了解到微服務的由來和其與傳統
單體應用模式的差別。這本電子書描述了許多關于微服務架構方面的内容。無論是在項目意義還是實施方面,你都能了解到微服務架構模式的優點與缺點。
我們先來看看為什麼要考慮使用微服務。
1.1、建構單體應用
我們假設,你正在開發一個打車應用,打算與 Uber 和 Hailo 競争。經過初步交流和需求收集,你開始手動或者使用類似 Rails、Spring Boot、Play 或者 Maven 等平台來生成一個新項目。
新應用有一個子產品化的
六邊形架構,如圖 1-1 所示:
(
https://github.com/DocsHome/microservices/blob/master/resources/1-1.png)應用的核心是由子產品實作的業務邏輯,這些子產品定義了服務、領域對象和事件。圍繞核心的是與外部世界接口對接的擴充卡。擴充卡示例包括資料庫通路元件、生産和消費消息的消息元件和暴露了 API 或實作了一個 UI 的 web 元件。
盡管應用有一個邏輯子產品化架構,但它被作為一個單體進行打包和部署。實際格式取決于應用所用的語言和架構。比如,許多 Java 應用被打包成 WAR 檔案部署在如 Tomcat 或者 Jetty 之類的應用伺服器上。還有部分 Java 應用被打包成自包含(self-contained)的可執行 JAR。類似地,Rails 和 Node.js 應用被打包成有一定層次結構的目錄。
使用這種風格編寫的應用很常見,也很容易開發,因為我們的 IDE 和其他工具本身就專注于建構單體應用。這些應用也很容易測試,你可以通過簡單地啟動并使用如 Selenium 測試包來測試 UI 以輕松地實作端到端(end-to-end)測試。單體應用同樣易于部署,你隻需拷貝打包好的應用到伺服器上。你還可以通過運作多個副本并結合負載均衡來擴充應用。在項目的早期階段,它可以良好地運作。
1.2、走向單體地獄
不幸的是,這種簡單的方式有很大的局限性。成功的應用有一個趨勢,随着時間推移而變得越來越臃腫。你的開發團隊在每個沖刺階段都要實作更多的使用者需求,這意味着需要添加許多行代碼。幾年之後,小而簡單的應用将逐漸成長成一個
龐大的單體。為了給出一個極端示例,我最近和一位開發者做了交談,他正在編寫一個工具,該工具用于從他們的數百萬行代碼(lines of code,LOC)應用中分析出數千個 JAR 之間的依賴。我相信這是大量開發者在多年齊心協力下創造出的野獸。
一旦應用成為一個龐大、複雜的單體,你的開發組織可能會陷入了一個痛苦的境地,靈活開發和傳遞的任何一次嘗試都将原地徘徊。一個主要問題是應用實在非常複雜,它對于任何一個開發人員來說都顯得過于龐大,這可以了解。而最終,正确修複 bug 和實作新功能變得非常困難而耗時。此外,這種趨勢就像是往下的螺旋。如果基礎代碼都令人難以了解,那麼改變也不會變得正确,你最終得到的将是一個巨大的
泥球。
應用的規模也将減緩發展。應用越大,啟動時間越長。我
調查過開發者們的單體應用的大小和性能,一些報告的啟動時間為 12 分鐘。我也聽說過應用啟動需要 40 分鐘以上的怪事。如果開發人員經常要重新開機應用伺服器,那麼很大一部分時間都是在等待中度過,他們的生産力将受到限制。
另一個大問題是,複雜的單體應用本身就是持續部署的障礙。如今,SaaS 應用發展到了可以每天多次将變更推送到生産環境。這對于複雜的單體來說非常困難,因為你需要重新部署整個應用才能更新其中任何一部分。聯想我之前提到的漫長啟動過程,這也不會是什麼好事。此外,因變更所産生的影響通常不是很明确,你很可能需要做大量的手工測試。是以,持續部署是不可能做到的。
當不同子產品存在資源需求沖突時,單體應用可能會難以擴充。例如,一個子產品可能會執行 CPU 密集型圖像處理邏輯,理想情況下是部署在
Amazon EC2 Compute Optimized 執行個體中。另一個子產品可能是一個記憶體資料庫,最适合部署到
EC2 Memory-optimized 執行個體。然而,由于這些子產品被部署在一起,你必須在硬體選擇上做出妥協。
單體應用的另一個問題是可靠性。因為所有子產品都運作在同一程序中。任何子產品的一個 bug,比如記憶體洩漏,都可能會拖垮整個程序。此外,由于應用的所有執行個體都是相同的,錯誤将影響到整個應用的可用性。
最後同樣重要的是,單體應用使得采用新架構和語言變得非常困難。例如,我們假設你有 200 萬行代碼使用了 XYZ 架構編寫。如果使用較新的 ABC 架構來重寫整個應用,這将非常昂貴(時間和成本方面),即使架構非常好。是以,這對于新技術采用是一個非常大的障礙。在項目開始時,你無論選擇何種新技術都會感到困擾。
總結一下:你有一個成功的關鍵業務應用,它已經發展成為一個隻有少數開發人員能夠了解的巨大單體。它使用了過時、非生産性技術編寫,使得招聘優秀開發人員變得非常困難。應用變得難以擴充,不可靠。是以靈活開發和應用傳遞是不可能的。
那麼你能做些什麼呢?
1.3、微服務 — 解決複雜問題
許多如 Amazon、eBay 和
Netflix這樣的組織,已經采用現在所謂的
解決了這個問題,而不是建構一個臃腫的單體應用。它的思路是将應用分解成一套較小的互連服務。
一個服務通常實作了一組不同的特性或功能,例如訂單管理、客戶管理等。每個微服務都是一個迷你應用,它自己的六邊形架構包括了業務邏輯以及多個擴充卡。
一些微服務會暴露一個供其他微服務或應用用戶端消費的 API。其他微服務可能實作一個 web UI。在運作時,每個執行個體通常是一個雲虛拟機(virtual machine,VM)或者一個 Docker 容器。
例如,前面描述的系統可能分解成如圖 1-2 所示:
應用的每個功能區域現在都由自己的微服務實作。此外,Web 應用被劃分為一組更簡單的應用。例如,以我們的計程車為例,一個是乘客的應用,一個是司機的應用。這樣更容易為特定的使用者、司機、裝置或者專門的用例部署不同的場景。每個後端服務暴露一個 REST API,大部分服務消費的 API 由其他服務提供。例如,Driver Management 使用了 Notification 伺服器來給司機發送一個可選路線通知。UI 服務調用了其他服務來渲染頁面。服務也可以使用異步、基于消息的通信。本電子書後面将會
更加詳細介紹服務間通信。
一些 REST API 也暴露給移動端應用供司機和乘客使用。然而,應用不能直接通路後端服務。相反,他們之間的通信是由一個稱為
API 網關(API Gateway)的中介負責。API 網關負責負載均衡、緩存、通路控制、API 度量和監控,
可以使用 NGINX 來實作 第二章将詳細讨論 API 網關。
微服務架構模式相當于此伸縮立方的 Y 軸坐标,此立方是一個來自
《架構即未來》的三維伸縮模型。另外兩個坐标軸是由運作多個相同應用副本的負載均衡器組成的 X 軸坐标和 Z 軸坐标(或資料分區),其中請求的屬性(例如,一行記錄的主鍵或者客戶辨別)用于将請求路由到特定的伺服器。
應用通常将這三種類型的坐标方式結合一起使用。Y 軸坐标将應用分解成微服務,如圖 1-2 所示。
在運作時,X 坐标軸上運作着服務的多個執行個體,每個服務配合負載均衡器以滿足吞吐量和可用性。某些應用也有可能使用 Z 坐标軸來進行分區服務。圖 1-4 展示了如何用 Docker 将 Trip Management 服務部署到 Amazon EC2 上運作。
在運作時,Trip Management 服務由多個服務執行個體組成,每個服務執行個體是一個 Docker 容器。為了實作高可用,容器在多個雲虛拟機上運作。服務執行個體之前是一個
類似 NGINX 的負載均衡器,用于跨執行個體分發請求。負載均衡器也可以處理其他問題,如
緩存、
通路控制 API 度量和
監控微服務架構模式明顯影響到了應用與資料庫之間的關系,與其他共享單個資料庫模式(schema)的服務有所不同,其每一個服務都有自己的資料庫模式。一方面,這種做法與企業級資料庫資料模型的思想相背,此外,它經常導緻部分資料備援。然而,如果你想從微服務中受益,每一個服務都應該有自己的資料庫模式,因為它能實作松耦合。圖 1-5 展示了資料庫架構示例應用。
每個服務都擁有各自的資料庫。而且,服務可以使用一種最适合其需求、号稱多語言持久架構(polyglot persistence architecture)的資料庫。例如,Driver Management,要找到與潛在乘客接近的司機,就必須使用支援高效地理查詢的資料庫。
從表面上看,微服務架構模式類似于 SOA。微服務是由一組服務組成。然而,換另一種方式去思考微服務架構模式,它是沒有商業化的 SOA,沒有內建
Web 服務規範(WS-)和企業服務總線(Enterprise Service Bus,ESB)。基于微服務的應用支援更簡單的輕量級協定,例如,REST,而不是 WS-。它們也盡量避免使用 ESB,而是實作微服務本身有類似 ESB 的功能。微服務架構也拒絕了 SOA 的其他部分,例如,資料通路
規範模式概念。
1.4、微服務的優點
微服務架構模式有許多非常好的地方。第一,它解決了複雜問題。它把可能會變得龐大的單體應用分解成一套服務。雖然功能數量不變,但應用已經被分解成可管理的塊或者服務。每個服務都有一個明确的邊界定義方式,如遠端過程調用(RPC)驅動或消息驅動 API。微服務架構模式強制一定程度的子產品化,實際上,使用單體代碼來實作是極其困難的。是以,使用微服務架構模式,個體服務能被更快地開發,并且易于了解和維護。
第二,這種架構使得每個服務都可以由一個團隊獨立專注開發。開發者可以自由選擇任何符合服務 API 契約的技術。當然,更多的組織是希望通過技術選型限制來避免完全混亂的狀态。然而,這種自由意味着開發人員不再有可能在這種自由的新項目開始時使用過時的技術。當編寫一個新服務時,他們可以選擇目前的技術。此外,由于服務較小,使用目前技術重寫舊服務将變得更加可行。
第三,微服務架構模式可以實作每個微服務獨立部署。開發人員根本不需要去協調部署本地變更到服務。這些變更一經測試即可立即部署。比如,UI 團隊可以執行 A/B 測試,并快速疊代 UI 變更。微服務架構模式使得持續部署成為可能。
最後,微服務架構模式使得每個服務能夠獨立擴充。你可以僅部署滿足每個服務的容量和可用性限制的執行個體數目。此外,你可以使用與服務資源要求最比對的硬體。例如,你可以在 EC2 Compute Optimized 執行個體上部署一個 CPU 密集型圖像處理服務,并且在 EC2 Memory-optimized 執行個體上部署一個記憶體資料庫服務。
1.5、微服務的缺點
就像 Fred Brooks 大約在 30 年前寫的
《人月神話》中說的,沒有銀彈。與其他技術一樣,微服務架構模式也存在着缺點。其中一個缺點就是名稱本身。微服務這個術語的重點過多偏向于服務的規模。事實上,有些開發者主張建構極細粒度的 10 至 100 LOC(代碼行)服務,雖然這對小型服務來說可能比較好,但重要的是,小型服務隻是一種手段,而不是主要目标。微服務的目标在于充分分解應用以友善應用靈活開發和部署。
微服務的另一個主要缺點是由于微服務是一個分布式系統,其使得整體變得複雜。開發者需要選擇和實作基于消息或者 RPC 的程序間通信機制。此外,由于目标請求可能很慢或者不可用,他們必須要編寫代碼來處理局部故障。雖然這些并不是很複雜、高深,但子產品間通過語言級方法/過程調用互相調用,這比單體應用要複雜得多。
微服務的另一個挑戰是分區資料庫架構。更新多個業務實體的業務事務是相當普遍的。這些事務在單體應用中的實作顯得微不足道,因為單體隻存在一個單獨的資料庫。在基于微服務的應用中,你需要更新不同服務所用的資料庫。通常不會選擇分布式事務,不僅僅是因為
CAP 定理。他們根本不支援如今高度可擴充的 NoSQL 資料庫和消息代理。你最後不得不使用基于最終一緻性的方法,這對于開發人員來說更具挑戰性。
測試微服務應用也很複雜。例如,使用現代架構如 Spring Boot,你隻需要編寫一個測試類來啟動一個單體 web 應用并測試其 REST API。相比之下,一個類似的測試類對于微服務來說需要啟動該服務及其所依賴的所有服務,或者至少為這些服務配置存根。再次強調,雖然這不是一件高深的事,但不要低估了這樣做的複雜性。
微服務架構模式的另一個主要挑戰是實作了跨越多服務變更。例如,我們假設你正在實作一個修改服務 A、服務 B 和 服務 C 的需求,其中 A 依賴于 B,且 B 依賴于 C。在單體應用中,你可以簡單地修改相應的子產品、整合變更并一次性部署它們。相反,在微服務中你需要仔細規劃和協調變更到每個服務。例如,你需要更新服務 C,然後更新服務 B,最後更新服務 A。幸運的是,大多數變更隻會影響一個服務,需要協調的多服務變更相對較少。
部署基于微服務的應用也是相當複雜的。一個單體應用可以很容易地部署到基于傳統負載均衡器的一組相同伺服器上。每個應用執行個體都配置有基礎設施服務的位置(主機和端口),比如資料庫和消息代理。相比之下,微服務應用通常由大量的服務組成。例如,據
Adrian Cockcroft了解到,Hailo 擁有 160 個不同的服務,Netflix 擁有的服務超過 600 個。
每個服務都有多個運作時執行個體。還有更多的移動部件需要配置、部署、擴充和監控。此外,你還需要實作
服務發現機制,使得服務能夠發現需要與之通信的任何其他服務的位置(主機和端口)。相比較,傳統的基于票據(ticket-based)和手動的操作方式無法擴充到如此複雜的程度。是以,要成功部署微服務應用,需要求開發人員能高度控制部署方式和高度自動化。
一種自動化方式是使用現成的平台即服務(PaaS),如
Cloud Foundry。PaaS 為開發人員提供了簡單的方式來部署和管理他們的微服務。它讓開發人員避開了諸如采購和配置 IT 資源等煩惱。同時,配置 PaaS 的系統人員和網絡專業人員可以確定達到最佳實踐以落實公司政策。
自動化微服務部署的另一個方式是開發自己的 PaaS。一個普遍的起點是使用叢集方案,如
Kubernetes,與 Docker 等容器技術相結合。在本書最後我們将看到如 NGINX 的
基于軟體的應用傳遞方式是如何在微服務級别輕松做到緩存處理、通路控制、API 度量和監控,這些可以幫助解決此問題。
1.6、總結
建構複雜的微服務應用本質上是困難的。單體架構模式隻适用于簡單、輕量級的應用,如果你使用它來建構複雜應用,你最終會陷入痛苦的境地。對于複雜、持續發展的應用而言,微服務架構模式是一個更好的選擇。盡管它存在着缺點和挑戰。
在後面的章節中,我将介紹微服務架構的方方面面并探讨諸如服務發現、服務部署方案以及将單體應用重構為服務的政策。