微服務架構的優勢與不足

編者的話|本文來自 Nginx 官方博客,是微服務系列文章的第一篇,主要探討了傳統的單體式應用的不足,以及微服務架構的優勢與挑戰。

作者介紹:Chris Richardson,是世界著名的軟件大師,經典技術著作《POJOS IN ACTION》一書的作者,也是 cloudfoundry.com 最初的創始人,Chris Richardson 與 Martin Fowler、Sam Newman、Adrian Cockcroft 等並稱爲世界十大軟件架構師。

Chris Richardson 所著所有文章已獨家授權 DaoCloud 翻譯並刊載。

本期內容


微服務在當下引起廣泛關注,成爲文章、博客、社交媒體討論和大會演講的熱點;在 Gartner 的 “Hype Cycle” 上排名也非常靠前。與此同時,在軟件社區也有人質疑微服務並非新事物。反對者認爲微服務只是 SOA (Service Oriented Architecture)的二度包裝。然而,無論是追捧還是質疑,微服務架構擁有巨大優勢,尤其是它讓敏捷開發和複雜的企業應用交付成爲可能。

本系列包含 7 篇文章,介紹了微服務的設計、構建和部署,並與傳統的單體架構進行了比較。本系列將分析微服務架構的各種因素,你也將瞭解微服務架構模型的優劣、是否適合你的項目,以及如何應用。

Chris Richardson 微服務系列全 7 篇:

1. 微服務架構概念解析

2. 構建微服務架構:使用 API Gateway

3. 深入微服務架構的進程間通信

4. 服務發現的可行方案以及實踐案例

5. 微服務的事件驅動數據管理

6. 選擇微服務部署策略

7. 將單體應用改造爲微服務

首先讓我們瞭解爲何要將微服務納入考量。

構建單體應用

假設我們要開發一款全新的與 Uber 和 Hailo 競爭的打車軟件。在前期的會議和需求整理後,你要麼需要手動創建一個新項目,要麼可以使用 Rails、Spring Boot、Play 或者 Maven 來生成。這個新應用可能採用了六邊形架構模塊,如下圖所示:

Richardson-microservices-part1-1_monolithic-architecture

應用的核心是商業邏輯,它由定義服務、域對象和事件各模塊來完成。各種適配器圍繞核心與外部交互。適配器包括數據庫訪問組件、生成和 consume 信息的消息組件,以及提供 API 或者 UI 訪問支持的 web 模塊。

儘管擁有邏輯縝密的模塊化設計,整個應用仍然以整體打包和部署,實際格式依賴於應用的語言和框架。譬如,許多 Java 應用被打包爲 WAR 文件,部署在 Tomcat 或者 Jetty 這樣的應用服務器。有些 Java 應用本身就是包涵 JARs 的軟件包。與此類似,Rails 和 Node.js 應用也通過目錄層級打包。

採用此種風格的應用非常普遍。由於 IDE 和其他工具擅長構建單一應用,這類應用也易於部署。這類應用也非常容易測試。你可以非常輕鬆地進行端到端測試,使用 Seleniu 測試 UI 。整體應用也便於部署,只需將軟件包複製到服務器。你也可以通過運行多個包和負載均衡實現擴展。在項目早期這麼做非常有效。

踏入單體架構的地獄

很不幸,這一簡單的方法有着巨大的侷限。成功的應用最終會隨着時間變得巨大。在每個 sprint 階段,開發團隊都會新加許多行代碼。幾年後,原本小而簡單的應用會變得臃腫。舉個極端的例子,我最近與一位開發者交流,他正在開發一款小工具,來分析他們應用(包括幾百萬行代碼)中的幾千個 JARs 的依賴。我相信每年都會有大量開發者不遺餘力地對付這種麻煩。

一旦你的應用變得龐大、複雜,你的開發團隊將飽受折磨,苦苦掙扎于敏捷開發和交付。一大原因就是應用已經格外複雜,龐大到任何一個開發者都無法完全理解。最後,修復 bug 和實施新功能也就極其困難且耗時頗多。更可怕的是,這是一個向下的螺旋發展。代碼庫越難理解,正確的修改就越難。最後你會深陷龐大的、無法估量的泥淖之中。

而這種應用的尺寸也會拖慢開發進度。應用越大,啓動時間越長。譬如在最近的調查中,不少開發者指出啓動時間長達 12 分鐘。我也聽說有的應用啓動時間居然得 40 分鐘。如果開發者不得不頻繁重啓應用服務器,那大量時間就被浪費,生產效率也飽受其害。

龐大且複雜的單體應用的另一大問題就是難以進行持續部署。現在, SaaS 應用的發展水平足以在單日內多次將修改推送到生產環境。然而要讓複雜的單個應用達到此水平卻極爲棘手。想更新應用的單個部分,必須重新部署整個應用,漫長的啓動時間更是雪上加霜。另外,由於不能完全預見修改的影響,你不得不提前進行大量人工測試。結果就是,持續部署變得不可能。

如果單體應用的不同模塊在資源需求方面有衝突的話,那應用的擴展也很難。比如,模塊之一需要執行 CPU-intensive 圖像處理邏輯,最好部署到 AWS 的 EC2 Compute Optimized instances;而另一模塊需要內存數據庫,最好適配 EC2 Memory-optimized instances。由於這兩個模塊需要共同部署,你不得不在在硬件選擇方面做妥協。

單體應用的另一問題就是可靠性。由於所有模塊都運行在同一進程中,任何模塊中的一個 bug,比如內存泄漏都可能弄垮整個進程;此外,由於應用中的所有實例都是唯一,這個 bug 將影響整個應用的可用性。

最後,單體應用會讓採用新框架和語言極其困難。舉例來說,你有兩百萬行使用 XYZ 框架的代碼,如果要使用 ABC 框架重寫代碼,無論時間還是成本都將非常高昂,即便新框架更好。這也就成爲使用新技術的阻礙。

總結:這個一開始曾經成功關鍵業務應用,最終卻變成一個臃腫的、無法理解的龐然大物。它使用老舊、陳腐、低效的技術,幾乎吸引不到出色的開發者。這個應用非常難於擴展,也不穩定可靠。最終,敏捷開發和交付幾乎成爲不可能。

你該何去何從?

微服務——直擊痛點

諸如亞馬遜、eBay、Netflix 等公司已經通過採用微服務架構範式解決了上文(第一部分)提到的問題。不同於構建單一、龐大的應用,微服務架構將應用拆分爲一套小且互相關聯的服務。

一個微服務一般完成某個特定的功能,比如訂單管理、客戶管理等。每個微服務都是一個微型應用,有着自己六邊形架構,包括商業邏輯和各種接口。有的微服務通過暴露 API 被別的微服務或者應用客戶端所用;有的微服務則通過網頁 UI 實現。在運行時,每個實例通常是一個雲虛擬機或者 Docker 容器。

對於前文所述的系統,一種可能的系統分解圖如下:

Richardson-microservices-part1-2_microservices-architecture

應用的每個功能區都由其自身微服務實施。此外,整個網頁應用被拆分爲一套簡單的網頁應用(比如我們的打車軟件拆分爲乘客應用和司機應用),從而能夠輕鬆地針對特定用戶、設備或者用戶案例進行單獨部署。

每個後端服務包括一個 REST API 和由其它服務提供的服務消耗 API。例如,司機管理服務使用“通知”服務器來告知司機即將的行程。UI 服務喚醒其它服務,從而呈現網頁。這些服務也可能用到基於信息的異步通信。內部服務通信會在本系列文章中詳述。

有的 REST API 也對司機和乘客的移動應用開放。這些應用並不能直接訪問後端服務器,相反,通信由名爲 API Gateway 的中間人調解。AIP Gateway 負責負載均衡、緩存、訪問控制、API 計費、監控等,通過 NGINX 高效實施。本系列的後續文章將會講解 API Gateway。

Richardson-microservices-part1-3_scale-cube

上圖是 Scale Cube 的 3D 模型,來自《The Art of Scalability》 一書。微服務架構範式對應 Y 軸,X 軸由負載均衡器後端運行的多個應用副本組成,Z 軸(數據分割)將需求路由到相關服務。

應用通常同時使用這三種不同類型的擴展。Y 軸擴展將應用分解爲如圖一(https://www.nginx.com/wp-content/uploads/2015/05/Graph-031-e1431992337817.png)所示的微服務。在運行時維度,X 軸擴展在輸出和可用性的負載均衡後運行多個實例。部分應用會使用 Z 軸擴展來對服務進行數據分割。下圖展示了行程管理服務(Trip Management)是如何使用 Docker 部署到 AWS EC2 上的。

Richardson-microservices-part1-4_dockerized-application

在運行時,行程管理服務包括多個服務實例,每個服務實例都是一個 Docker 容器。爲了實現高可用性,這些容器運行在多個雲虛擬機上。在應用實例前面是 NGINX 這樣的負載均衡,將請求分發給全部實例。負載均衡也可以處理緩存、訪問控制、 API 測量和監控等。

微服務架構範式對應用和數據庫的關係影響巨大。每個服務都有自身的數據庫計劃,而不與其它服務共享同一個數據庫。一方面,這個方法類似企業級數據模型。同時,它也導致部分數據的重複。然而,要想從微服務中獲益,爲每個服務提供單個的數據庫計劃就非常必要,這能保證鬆散耦合。下面的圖表展示了示例應用的數據庫架構。

intro-microservices

每個服務都有其自己的數據庫。此外,單個服務可以使用符合自己需要的特定類型的數據庫,即多語言一致性架構。例如,爲了發現附近乘客,駕駛員管理服務必須使用高效支持地理位置請求的數據庫。

表面上看,微服務架構範式與 SOA 非常類似,這兩種架構都包括一套服務。然而,微服務架構範式被看作不包含某些功能的 SOA 。這些功能包括網絡服務說明( WS-* )和 Enterprise Service Bus (ESB) 的商品化和請求包。基於微服務的應用更青睞 REST 這樣簡單的、輕量級的協議,而不是 WS-* 。他們也極力避免在微服務中使用 ESBs 及類似功能。微服務架構範式也拒絕 SOA 的其它部分,比如 canonical schema 的概念。

微服務架構的好處

微服務架構模式有很多好處。首先,通過分解巨大單體應用爲多個服務方法解決了複雜性問題。在功能不變的情況下,應用被分解爲多個可管理的分支或服務。每個服務都有一個用 RPC- 或者消息驅動 API 定義清楚的邊界。微服務架構模式給採用單體式編碼方式很難實現的功能提供了模塊化的解決方案,由此,單個服務很容易開發、理解和維護。

第二,這種架構使得每個服務都可以有專門開發團隊來開發。開發者可以自由選擇開發技術,提供 API 服務。當然,許多公司試圖避免混亂,只提供某些技術選擇。然後,這種自由意味着開發者不需要被迫使用某項目開始時採用的過時技術,他們可以選擇現在的技術。甚至於,因爲服務都是相對簡單,即使用現在技術重寫以前代碼也不是很困難的事情。

第三,微服務架構模式使得每個微服務獨立部署,開發者不再需要協調其它服務部署對本服務的影響。這種改變可以加快部署速度,譬如 UI 團隊可以採用 AB 測試並快速部署變化。微服務架構模式使得持續化部署成爲可能。

最後,微服務架構模式使得每個服務獨立擴展。你可以根據每個服務的規模來部署滿足需求的實利。甚至於,你可以使用更適合於服務資源需求的硬件。比如,你可以在 EC2 Compute Optimized instances 上部署 CPU 敏感的服務,而在 EC2 memory-optimized instances 上部署內存數據庫。

微服務架構的不足

Fred Brooks 在 30 年前寫道 “there are no silver bullets”,像任何其它科技一樣,微服務架構也有不足。其中一個跟他的名字類似,“微服務”強調了服務大小,實際上,有一些開發者鼓吹建立稍微大一些的,10-100 LOC服務組。儘管小服務更樂於被採用,但是不要忘了微服務只是結果,而不是最終目的。微服務的目的是有效的拆分應用,實現敏捷開發和部署。

另外一個不足之處在於,微服務應用是分佈式系統,由此會帶來固有的複雜性。開發者需要在 RPC 或者消息傳遞之間選擇並完成進程間通訊機制。此外,他們必須寫代碼來處理消息傳遞中速度過慢或者不可用等局部失效問題。當然這並不是什麼難事,但相對於單體式應用中通過語言層級的方法或者進程調用,微服務下這種技術顯得更復雜一些。

另外一個關於微服務的挑戰來自於分區的數據庫架構。同時更新多個業務主體的事務很普遍。這種事務對於單體式應用來說很容易,因爲只有一個數據庫。在微服務架構應用中,需要更新不同服務所使用的不同的數據庫。使用分佈式事務並不一定是好的選擇,不僅僅是因爲 CAP 理論,還因爲當前高擴展性的 NoSQL 數據庫和消息傳遞中間件並不支持這一需求。最終你不得不使用一個最終一致性的方法,從而對開發者提出了更高的要求和挑戰。

測試一個基於微服務架構的應用也是很複雜的任務。比如,對於採用流行的 Spring Boot 架構的單體式 web 應用,測試它的 REST API,是很容易的事情。反過來,同樣的服務測試需要啓動與它有關的所有服務(至少需要這些服務的 stubs)。再重申一次,不能低估了採用微服務架構帶來的複雜性。

另外一個挑戰在於,微服務架構模式應用的改變將會波及多個服務。比如,假設你在完成一個案例,需要修改服務A、B、C,而 A 依賴 B,B 依賴 C。在單體應用中,你只需要改變相關模塊,整合變化,部署就好了。對比之下,微服務架構模式就需要考慮相關改變對不同服務的影響。比如,你需要更新服務 C,然後是 B,最後纔是 A。幸運的是,許多改變一般隻影響一個服務,而需要協調多服務的改變很少。

部署一個微服務應用也很複雜,一個單體應用只需要在複雜均衡器後面部署各自的服務器就好了。每個應用實例是需要配置諸如數據庫和消息中間件等基礎服務。相比之下,一個微服務應用一般由大批服務構成。根據 Adrian Cockcroft 的分享,Hailo 由 160 個不同服務構成,而 NetFlix 則超過 600 個服務。每個服務都有多個實例,這就形成大量需要配置、部署、擴展和監控的部分。除此之外,你還需要完成一個服務發現機制(後續文章中發表),以用來發現與它通訊服務的地址(包括服務器地址和端口)。傳統的解決問題辦法並不能解決這麼複雜的問題。最終,成功部署一個微服務應用需要開發者有足夠的控制部署方法,並高度自動化。

自動化的方法之一是使用譬如 Cloud Foundry 這樣的 PaaS 服務。PaaS 能讓開發者輕鬆部署和管理微服務,讓他們無需爲獲取並配置 IT 資源勞神。同時,配置 PaaS 的系統和網絡專家可以採用最佳實踐和策略來簡化這些問題。另外一個自動部署微服務應用的方法是開發自己的基礎 PaaS 系統。通常的起步方式是 Mesos 或 Kubernetes 這樣的集羣管理方案,配合 Docker 使用。作爲一種基於軟件的應用交付方法,NGINX 能夠方便地在微服務層面提供緩衝、權限控制、API 統計、以及監控。我們會在後續的文章中分析它如何解決這些問題。

總結

構建複雜的應用的確非常困難。單體式的架構更適合輕量級的簡單應用。如果你用它來開發複雜應用,那真的會很糟糕。微服務架構模式可以用來構建複雜應用,當然,這種架構模型也有自己的缺點和挑戰。

在後續的博客中,我會深入探索微服務架構模式,並討論多個話題,包括服務發現、服務部署選擇、以及將單體應用拆分爲多個服務的策略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章