【編者的話】 本文從對微服務的誤解誤用切入,探討什麼是微服務,如何切分微服務。提出了結合傳統的DDD,領域驅動設計的理念來幫助定義微服務的邊界,值得一讀。
過去的三年裡,我的工作是Golang,以及雲原生的分散式系統架構,包括微服務的諮詢師和培訓師。在微服務的諮詢經驗裡,在不同的客戶那裡因為大家對微服務的誤解,我遇到過很多奇怪的事情。我在印度工作,至少在這裡,微服務是當下IT行業裡最被誤解的技術。每個人都在談微服務。很多人說他們的應用程式是基於微服務架構的。
對微服務的誤解
這裡有幾種我聽說過的微服務相關的誤解:
- 構建HTTP服務,使用Docker容器執行它,並且使用Kubernetes做叢集管理,這就是微服務。
- 使用API Gateway和服務發現以及服務registry,這就是微服務。
- 使用Spring Boot框架構建HTTP服務,並且使用Netflix OSS,這是微服務。(這來自於Java社群)
- 使用Azure Service Fabric構建並且執行應用程式,這是微服務。(這來自於.Net社群)
- 構建輕量的RESTful API,這是微服務。
- 有很多框架聲稱是微服務框架。使用這些框架的任意一種來構建應用程式,這就是微服務。
有關微服務的誤解還有很多很多不在上述列表中。你可能會使用上述列表裡的一些技術來構建微服務,但是使用了某些工具和框架就說自己是微服務,這是沒有道理的。
微服務是分散式的系統架構,但是那些從來沒有開發過分散式系統的開發團隊,正在試影象學習新框架那樣來學習它,並且基於一些誤解來構建應用程式,然後聲稱自己的應用是基於微服務的。可以看到很多文章的標題都是使用X(Go,Node.js,Java等等)語言,Docker和Kubernetes來構建微服務,講解構建HTTP服務,在Docker容器裡執行,並且使用Kubernetes管理叢集,認為這就是微服務。最近在一次我的微服務培訓裡,一個認為自己是很有經驗的架構師的人,告訴我他是微服務大師,知道有關微服務的所有東西。在我和他的討論中,我意識到他從來沒有聽說過DDD和Bounded Context, Event Sourcing, CQRS, gRPC等等,都是從我這裡第一次聽到這些詞。一個從來沒有聽說過領域建模和DDD Bounded Context的人認為自己是微服務的超級大師。。。
和我探討過的很多創業公司,都宣傳他們遵守微服務架構。在和他們的討論中,我意識到他們稱其為微服務,是因為他們的產品有2-5個執行著的應用程式,對於他們來說每個應用程式就是一個微服務。面向客戶的應用程式是一個微服務,管理應用是另一個微服務,後臺worker是微服務,RESTful API是另一個微服務。大家基於自己的理解以及自己習慣的方式來構建自己的微服務。讓人傷心的是有人甚至沒有意識到微服務是一種分散式系統架構。
什麼是微服務?
首先最為重要的是,微服務是構建分散式系統的架構風格。微服務解決了構建分散式系統的複雜度。在微服務架構裡,軟體系統由一系列互相獨立可部署,小的,模組化的服務組成。每個微服務圍繞一種小型業務能力而構建,由獨立團隊管理。微服務是某個小型業務能力的獨立可部署的元件,微服務架構是構建高度可擴充套件可進化的軟體系統的工程風格,由細粒度的微服務組成。直觀上,微服務是可以一起工作的小的,自治的服務。
在絕大多數情況下,一開始應用程式是個單體應用,所有業務能力都放在單個應用程式裡,執行在單個程式裡。這樣的應用程式很容易開發,但是很難有效地擴充套件,因為應用程式的每個元件都緊耦合在一起。在微服務的架構裡,我們通過組合一系列的自治服務來構建應用程式,每個服務執行在自己的程式裡。這樣,就可以很輕鬆地實現擴充套件——可以給不同的服務賦予不同的擴充套件能力,獨立地升級並且替代每個服務,同時獲得很多其他的架構優勢。
很多人認為微服務是統一的架構框架,是解決所有軟體工程問題的銀彈。因此大家問的問題類似於,微服務的安全是什麼樣的。微服務並非一種統一的架構框架,並不是軟體工程中所有問題的解決方案,但是它是構建分散式系統的一種方式,這裡的核心思想是服務模組化。也就是說,微服務是構建分散式系統的一種思路,需要多種架構方案來實現這樣的思路。微服務是已經使用很久的多種架構方案的演進。因此在真實的微服務裡,你仍然會使用之前使用過的多種架構方案。
容器化是構建並且執行微服務的很好的方式。如果可以將微服務打包進容器,那麼就可以無限擴充套件並且動態編排微服務,而不需考慮在哪裡執行。打包工具,比如Docker,rkt和編排工具,比如Kubernetes,都是容器化微服務的很好的技術。
DDD Aggregate,Bounded Context分離微服務
當從單體架構向微服務架構演進時,最大的問題是怎麼將微服務分離出來。如何將一個大型軟體系統分解為功能性的元件?微服務的規模多大合適?答案很簡單:領域建模。Sam Newman,在他的書“構建微服務”裡說,所有走向微服務的路都繞不開領域建模。Eric Evans寫的在2003年出版的“領域驅動設計:在軟體核心裡處理複雜度”是領域建模領域的經典書籍,提供了基於領域模型構建複雜軟體系統的指導概要。自從這本書出版之後,Domain-Driven Design (DDD,領域驅動設計)這一術語被業界廣泛接受,並且開始使用DDD作為構建軟體系統的方式。DDD,介紹了多種構建塊,比如Entity,Value Object,Service,Repository,Aggregate和Bounded Context。
Aggregate和Bounded Context是構建微服務的構建塊,將決定單個微服務的大小。通常業務實體,比如Order,Customer,Account是Aggregate,它是一張圖,包含根實體以及一個或多個其他實體和value物件,是一個最小單元。在微服務裡執行事務的更好的方式是通過aggregate實現持久化。Bounded Context是DDD的核心模式,通過將大模型分解為不同的Bounded Context,從而將業務問題從邏輯上分解成多個子域。Bounded Context封裝了單個領域的細節,大型領域模型的子域,就可以剝離為單個微服務。構建微服務的常見戰略就是基於每個Bounded Context構建微服務。每個微服務使用自己的資料庫來持久化某個領域模型的Bounded Context。單個Bounded Context可以包含很多aggregate root,或者單個Bounded Context可以包含一個aggregate root。Order的aggregate root,包含實體包括OrderLine,Customer和Product,value object包括ShippingAdress,PaymentMethod等。在Order的aggregate root圖裡,Customer實體還可以作為aggregate root,因為它可以作為root實體,得到某個客戶的所有資訊。Aggregate和Bounded Context是微服務架構裡的重要概念,這是構建微服務的基礎塊,也將決定微服務的大小。簡單來說,微服務是圍繞Bounded Context的自治服務。
微服務裡的挑戰
微服務架構的確是構建分散式系統的絕佳方案。但是要記住微服務既不是銀彈也不是一種簡單的方案。即使使用了微服務來降低構建分散式系統的複雜度,構建分散式系統也絕不是一件輕鬆的事情。
當從單體應用程式轉向微服務架構時,需要解決很多實際的挑戰。比如,業務事務可能會分散為幾個微服務,因為基於Bounded Context將單體系統分解為多個自治服務。當需要管理資料一致性時,事務可能需要在很多微服務裡執行持久化。另一個挑戰是從多個資料庫裡查詢資料。在單體應用裡,可以從單個資料庫輕鬆地執行inner join查詢。因為單體資料庫在分解功能性元件時改成了多個資料庫,那麼就無法執行inner join,而必須從多個資料庫獲取資料。這時沒有任何中央化的資料庫。
使用Event Sourcing和CQRS構建可擴充套件的微服務
DDD Aggregate和Bounded Context是微服務的基礎構建塊,但是為構建分散式系統解決實際微服務挑戰而選擇具體架構時,DDD Aggregate上的事件驅動的互動系統是一種比較好的方案。這裡,我強烈推薦使用Event Sourcing,是事件為中心的架構,通過組合各種事件來構成應用程式的狀態。Event Sourcing處理不可變事件日誌的事件儲存,每條日誌(某個物件的一次狀態變更)代表一種應用程式的狀態。因為應用程式裡的每次狀態變更都作為不可變日誌來處理,你可以輕鬆地排查出應用程式的故障,也可以回到特定時間特定版本的應用程式狀態。事件儲存像版本控制系統一樣。在微服務的架構裡,我們可以將aggregate持久化成事件序列。事件即事實,代表系統裡發生的一些操作。這些是不可變的,不會變更或者回退。如果想要在系統裡做變更,需要往事件儲存裡寫入新的日誌來表示另一個事件集。事件的示例包括OrderCreated,OrderApproved, OrderShipped, OrderDelivered等等。在Event Sourcing架構裡,當你從某個微服務釋出一個事件時,其他的微服務可以響應這些事件,並且釋出另外的事件集。有時候,事件序列類似於Unix pipe。微服務系統裡的單個事務可能會延展到多個微服務裡,可以通過構建互動式的微服務來將事務作為事件序列來執行。一個aggregate的每次狀態變更都是一個事件,這是系統相關的不可變的事實。為了釋出事件讓其他微服務知道系統裡發生的事情,可以使用訊息系統,比如Apcera NATS,Kafka,RabbitMQ等等。我個人喜歡的選擇是Apcera NATS和Google的Cloud Pub/Sub。事件驅動,互動式的架構是構建大規模可擴充套件微服務的很好的架構方案。
當使用Event Sourcing將事件序列持久化時,你可能需要一種架構方案來做微服務的查詢。架構模式,Command Query Responsibility Segregation(CQRS)是實現微服務查詢的理想模式。顧名思義,CQRS將應用程式分為兩部分:Command來執行操作變更aggregate的狀態,Query為aggregate的檢視提供查詢模型。我們還可以使用不同的資料庫來做寫操作和查詢操作。這也讓你能夠通過將非規則資料裝載進讀模型的資料倉儲來提供高效能的查詢模型。NoSQL/NewSQL資料庫是很好的讀模型資料的儲存方案。
雖然Event Sourcing和CQRS,是使用微服務架構實現分散式系統的很好的模式,但是它並非銀彈,也有其自身的限制。你可能需要為一些型別的微服務系統的構建使用不同的架構風格。但是總的來說,我認為Event Sourcing,和CQRS的組合,是實現微服務系統的很好的方案。
使用gRPC實現基於API的程式間通訊
在微服務架構裡,可能需要進行很多微服務間的程式間通訊。在微服務間進行程式間通訊的兩種方案如下:
- 使用訊息系統的非同步的事件驅動架構
- 為每秒百萬次的API呼叫構建大規模高效能的API
通過使用帶訊息系統的Event Sourcing架構,你可以實現一種非同步的事件驅動的架構來管理aggregate的狀態。你還可能需要建立API以供微服務間通訊。當你用API執行微服務間的程式間通訊時,效能和可擴充套件性是非常重要的。應該通過構建高效能的API,從而讓大家感覺不到網路上正在發生很多通訊。當構建大規模可擴充套件的系統時,基於JSON的RESTful API不是很好的選擇,因為效能挑戰會很大,並且缺少暴露領域特定的操作為API的能力,因為RESTful系統是基於資源的概念的。這裡,gRPC,一種高效能,開源的遠端過程呼叫(RPC)框架,可以用來為微服務間的程式間通訊構建大規模可擴充套件的API。預設來說,gRPC使用Protocol Buffer作為介面定義語言(IDL),並且作為底層訊息交換格式。gRPC是Google的內部框架,Stubby的開源版本,它用來擴充套件支撐每秒100億次的API呼叫。gRPC是微服務架構裡基於API的程式間通訊的協議。
微服務的誤解和誤用
正如本文一開始所說,對於微服務有很多誤解,來自於多個開發者社群。我曾經收到過來自不同客戶的要求舉辦微服務workshop的請求,很多時候,客戶會提出課程大綱,這非常讓人吃驚,因為大綱上沒有任何關於微服務的東西。很多開發者社群將微服務和一些框架和工具繫結在一起。很多Java開發人員堅信使用Spring Boot構建RESTful API並且利用Netflix OSS的一些工具就是微服務,而另一些來自.NET社群的人認為使用Azure Service Fabric執行的應用程式是微服務。也有人問我RESTful服務和微服務的區別,因為很多人認為構建一些輕量的RESTful API就是微服務。另外一些人認為使用Spring Boot構建API是微服務。我也不知道從哪裡開始Spring Boot被誤解為微服務的。
我感覺到大家只是聽到了微服務這個詞,並且將它和一些工具以及框架聯絡在一起,並沒有理解這種變革性架構的真正精神和意圖。這樣的情況和架構模式,比如SOA以及工程實踐,比如Agile很是類似。我知道很多公司聲稱自己遵照敏捷工程實踐,僅僅因為他們使用了Scrum流程,而其實他們仍然以非常傳統的方式構建應用程式。TDD也是一樣,很多企業在編寫生產程式碼之後才寫單元測試,然後嘗試得到想要達到的測試覆蓋率,就認為自己在使用TDD開發應用程式。即使是一個小眾社群,但是很多人在引入技術,模式和實踐的時候,僅僅是為了聲稱使用了這些東西而已。
微服務的確是構建大規模可擴充套件應用程式的偉大的架構風格。微服務還適合構建網際網路規模的應用程式,比如Netflix,Uber,Amazon,eBay等等。但是它不是所有人都適合使用的,也並非銀彈。雖然微服務是構建大規模可擴充套件應用程式,包括面向大眾的網際網路級別的應用程式的正確的架構方案,但是對於大多數其他型別的應用程式,特別是構建企業級應用程式(有複雜的領域模型)來說,它可能是錯誤的選擇。我個人不推薦用微服務來做複雜的業務應用,因為這些應用有非常複雜的領域模型。但是微服務是領域模型沒那麼複雜的,大規模可擴充套件的,並且可擴充套件是最具挑戰因素,也是成功要素的應用程式的理想選擇。對於一些型別的應用程式,微服務架構風格的實現可能會帶來很多效能問題,並且增加了系統的複雜度,最終可能導致失敗。僅僅因為和熱門技術沾邊就使用微服務可能會給你的系統帶來很多副作用。
改進單體應用程式
在我的諮詢經驗裡,我認為在絕大多數用例裡,不需要使用微服務架構,適合大多數應用程式的更好的方案是折中方案。你可以使用混合方案來解決問題。你的企業可能並沒有Google,Amazon,Netflix,Uber,eBay,Square等公司這樣的技術能力。在你的公司裡你的問題是獨特的。架構方案必須要能夠解決問題,並且使用它解決你自己的獨特問題,而不是為了用而用。
你可以按照如下方式改進已有的單體應用程式:將單體應用程式分解為多個系統,而不是盲目遵守微服務的概要,選擇使用混合架構方案,使用DevOps文化以及現代化的CI/CD pipeline。微服務的最大優勢是模組化。當構建新的應用程式時,可以通過應用模組化系統設計原則,而不是微服務架構,來架構系統。比如,Go程式語言的打包生態系統讓你能夠使用更好的模組化思路來設計應用程式。如果之後你想要遷移到微服務來獨立擴充套件模組和團隊的話,該方案還讓你能更容易地遷移到微服務上。最好是從使用模組化系統設計原則實現單體應用開始,然後當系統和團隊發展時,可以在確實需要的時候轉向微服務架構。
要記住你的公司和產品是獨一無二的。不要成為技術,模式和實踐的盲目追隨者,使用折中方案解決自己的問題。為了使用技術而使用是沒有任何意義的。
原文連結:Microservices: Overview, Misinterpretations and Misuses(翻譯:崔婧雯)
===========================
譯者介紹
崔婧雯,現就職於IBM,高階軟體工程師,負責IBM WebSphere業務流程管理軟體的系統測試工作。曾就職於VMware從事桌面虛擬化產品的質量保證工作。對虛擬化,中介軟體技術,業務流程管理有濃厚的興趣。