- 微服務與事件驅動架構
- (加)亞當·貝勒馬爾
- 11807字
- 2021-10-25 15:14:18
第 1 章 為什么用事件驅動型微服務
媒介即信息。
——Marshall McLuhan
Marshall McLuhan 認為,影響人類并給社會帶來根本性變革的不是媒介的內容,而是與媒介的互動過程。在我們的集體參與下,報紙、廣播、電視、互聯網、即時通信和社交媒體改變了人類的互動方式以及社會結構。
對于計算機系統架構來說同樣如此。只需回顧計算機技術的發明史就會發現,網絡通信、關系型數據庫、大數據開發和云計算等顯著地改變了系統架構的構建方式和工作方式。其中的每一項發明不僅改變了技術在各軟件項目中的使用方式,也改變了各類組織、團隊和人們相互交流的方式。從中心化的大型機到分布式的移動應用,每種新的媒介都徹底改變了人們與計算的關系。
異步生產和消費事件的媒介已被現代技術徹底改變。這些事件如今可以被大規模地永久存儲,并且能被任何服務按需多次消費。可以根據需要輕松地獲取和釋放計算資源,這讓微服務的創建和管理變得簡單。微服務可以根據自身的需要存儲和管理數據,并且能夠達到之前僅限于基于批處理的大數據解決方案的數據規模。對事件驅動媒介的這些改進已經產生了深遠的影響,不僅改變了計算機體系結構,也徹底重塑了團隊、個人和組織創建系統和業務的方式。
1.1 什么是事件驅動型微服務
微服務和微服務類型的架構已經存在很多年了,它們有許多不同的形式和名字。面向服務的架構(service-oriented architecture,SOA)通常由多個相互直接同步通信的微服務構成。消息傳遞架構使用可被消費的事件在微服務之間進行異步通信。基于事件的通信當然不算新穎,但大規模并實時地處理大數據集是新的需求,而這要求對舊的架構類型進行改進。
在現代的事件驅動型微服務架構中,系統通過發布和消費事件來通信。這些事件并不會像消息傳遞系統中那樣在某次消費的時候就被銷毀,而是可被其他多個消費者按需消費。這是一個重要的區別,因為正是這個特性使得本書所要介紹的真正強大的模式成為可能。
微服務本身是微小且定制的,旨在幫助組織實現必要的業務目標。“微小”的一種典型定義是能夠在兩周內實現,另一種定義是服務應該(在概念上)像是出自一個人的頭腦。這些服務從輸入事件流中消費事件,運行它們特定的業務邏輯,并且可能發布自己的輸出事件,提供用于“請求–響應”訪問的數據,與某個第三方 API 通信或者執行其他必需的操作。正如本書將要詳細介紹的,這些服務可以是有狀態的,也可以是無狀態的;可以復雜,也可以簡單;可以被實現為長期運行的獨立應用程序,也可以通過“函數即服務”作為函數執行。
事件流和微服務的組合形成了貫穿整個組織的業務活動的相連圖。傳統的計算機體系結構由多個單體應用和單體應用間的通信組成,具有相似的圖形結構。這兩種圖形結構如圖 1-1 所示。
圖 1-1:微服務和單體應用的圖形結構
為了確定如何使這種圖形結構高效運行,要看兩個主要部分:節點和連接。本章會依次對它們進行介紹。
1.2 領域驅動設計和界限上下文
領域驅動設計由 Eric Evans 在他的同名圖書《領域驅動設計:軟件核心復雜性應對之道》1 中提出,書中介紹了一些構建事件驅動型微服務所必需的概念。鑒于已有豐富的文章(“What is the Domain Model in Domain Driven Design?”)、圖書(《實現領域驅動設計》)和博客資源討論了這一主題,本節僅做簡單介紹。
1該書已由人民郵電出版社出版,詳見圖靈社區。——編者注
以下概念支撐了領域驅動設計。
領域
某個企業所涉足并提供解決方案的問題空間。它涵蓋了這個企業必須關注的所有內容,包括規則、流程、想法、特定于業務的術語,以及所有跟這個問題空間相關的東西,無論該企業是否與之相關。領域的存在跟企業是否存在無關。
子領域
子領域是主領域的一個部分。每個子領域都聚焦于一個特定的職責子集,并且通常反映了企業的部分組織結構(如倉儲、銷售和工程部門)。一個子領域本身可視為一個領域。跟領域一樣,子領域也屬于問題空間。
領域(子領域)模型
這是一種用于商業目的的對實際領域的抽象。在領域中對企業最重要的部分和屬性被用來生成模型。一個企業的主領域模型可通過其提供給客戶的產品、客戶與產品交互的接口以及企業實現既定目標的各種流程和功能來識別。模型往往需要隨著領域的改變和業務優先級的變化而不斷完善。領域模型是解決方案空間的一部分,因為它是企業用來解決問題的框架。
界限上下文
界限上下文是與子領域相關的邏輯邊界,包括輸入、輸出、事件、需求、流程和數據模型。雖然在理想情況下,界限上下文會和子領域保持完全一致,但是歷史遺留的系統、技術債和第三方集成等因素通常會帶來一些例外。界限上下文也是解決方案空間的一個屬性,對微服務如何相互交互有著重要的影響。
界限上下文應該是高內聚的。上下文內部的運作應該是緊密相關的,絕大部分的通信發生在內部而不會跨越邊界。擁有高內聚的職責可以縮小設計范圍和簡化實現。
界限上下文之間的關聯應該是低耦合的,因為一個界限上下文內部的更改應該最小化或消除對相鄰上下文的影響。低耦合可以確保在某個上下文內的需求變更不會向相鄰的上下文傳播大量依賴變更。
1.2.1 運用領域模型和界限上下文
每個組織都會在其自身和外部世界之間形成一個單獨的領域。每個人在這個組織內的工作都在支持這個領域的需求。
領域可以被劃分為多個子領域,例如,對于以技術為中心的公司來說,會有一個工程部門、一個銷售部門和一個客戶支持部門。每個子領域有其自身的需求和職責,并且其本身可以進一步劃分。這個劃分的過程會不斷重復,直到子領域模型的顆粒度變得足夠細和具有可操作性,并且開發團隊可將其實現為小型的獨立服務。界限上下文是圍繞著這些子領域建立的,并且是創建微服務的基礎。
1.2.2 保持界限上下文與業務需求一致
產品的業務需求在其生命周期內發生變化是很平常的事情,這可能是由于組織變更或新特性的要求所致。很少有公司需要在沒有業務需求變更的情況下去變更既有產品的底層實現。這就是為什么界限上下文應該圍繞著業務需求而不是技術需求去構建。
保持界限上下文和業務需求一致,可以讓開發團隊以高內聚、低耦合的方式對微服務的實現做出變更。這為團隊提供了設計和實現特定業務需求解決方案的自主權,可以大大減少團隊之間的依賴,并且讓每個團隊都高度聚焦于自身需求。
相反,根據技術需求來調整微服務是有問題的。這種模式通常見于設計不當的同步點對點微服務和傳統的單體式計算系統中,在這些系統中不同的團隊負責應用程序中特定的技術分層。按照技術保持一致性的主要問題在于,它將實現業務功能的職責分散到不同的界限上下文中,這會涉及多個具有不同排期和職責的團隊。因為沒有一個團隊單獨對實現某個解決方案負責,所以每個服務都要跨越團隊和 API 邊界,這使得變更很困難,而且成本很高。一個看起來無害的變更、缺陷或者故障服務,卻可能對使用這種技術系統的所有服務的業務服務能力產生嚴重的連鎖反應。事件驅動型微服務很少采用保持技術一致性的做法,并且應該盡可能地完全避免這種做法。清除跨領域的技術方案和團隊間的相互依賴可以降低系統對變更的敏感度。
圖 1-2 展示了兩種場景:左邊的單一權責結構和右邊的跨領域權責結構。在單一權責結構中,團隊是完全圍繞著兩個獨立的業務需求(界限上下文)來組織的,并且可以完全控制其應用層和數據層。在右邊的圖中,團隊是根據技術需求來組織的,應用層和數據層是分開管理的。這就在不同的團隊之間建立了顯式的依賴,進而在不同的業務需求之間產生了隱式的依賴。
圖 1-2:對齊業務上下文與對齊技術上下文
圍繞著業務需求對事件驅動型微服務架構建模是更好的選擇,盡管這種方法也有一些折中之處。代碼可能會被復制多次,并且許多服務可能使用相似的數據訪問模式。產品開發者可能會嘗試與其他產品共享數據資源或耦合邊界的方式來減少復制行為。在這些例子中,從長遠來看,后續的緊耦合可能比重復代碼和存儲冗余數據帶來更高的成本。這些權衡點將在本書中詳細討論。
保持界限上下文之間的低耦合,并聚焦于最小化上下文之間的依賴。這將使得界限上下文的實現可以按需變更,而不會進一步破壞很多(或任何)其他系統。
此外,每個團隊可能都需要擁有全棧的專業知識,這個要求會因需要特定的技能集和訪問權限而變得復雜。組織應該實施最通用的需求,這樣這些垂直團隊就能進行自支持,同時可以根據需要在跨團隊中提供更專業的技能集。這些最佳實踐會在第 14 章中詳細介紹。
1.3 溝通結構
要達成目標,一個組織的團隊、系統和人員必須相互溝通。這些溝通形成了一個相互連接的依賴拓撲結構,被稱為溝通結構。溝通結構主要有 3 種,每一種都影響著企業的運作方式。
1.3.1 業務溝通結構
如圖 1-3 所示,業務溝通結構決定了團隊和部門之間的溝通方式,每個團隊和部門都由分配給它的主要需求和職責驅動。例如,工程部開發軟件產品,銷售部將其賣給客戶,支持部則負責確保顧客和客戶滿意。從主要業務部門到個人貢獻者的工作,團隊的組織及其目標的配置都遵循此結構。業務需求、需求在團隊間的分配以及團隊的組成都會隨著時間而改變,這會極大地影響業務溝通結構和實現溝通結構之間的關系。
圖 1-3:業務溝通結構的例子
1.3.2 實現溝通結構
實現溝通結構(參見圖 1-4)是由組織確定的子領域模型相關的數據和邏輯。它確立了業務流程、數據結構和系統設計,以便快速高效地執行業務操作。因為實現重新定義的業務需求需要重寫邏輯,所以這會導致業務溝通結構靈活性的折中。這些重寫通常是對子領域模型和相關代碼的迭代改進,隨著時間的推移,它們反映了為滿足新的業務需求而在實現上進行的演進。
圖 1-4:實現溝通結構的例子
軟件工程中對應實現溝通結構的典型例子是單體數據庫應用程序。應用程序中的業務邏輯通過函數調用或共享狀態來進行內部通信。繼而,這類單體應用被用來滿足業務溝通結構所確定的業務需求。
1.3.3 數據溝通結構
數據溝通結構(參見圖 1-5)是在業務之間,特別是實現之間進行數據通信的過程。雖然在日常事務中經常使用包括電子郵件、即時通信和會議的數據溝通結構來傳達業務的變更,但在大部分軟件實現中,數據溝通結構是缺失的。它的作用通常臨時體現在系統跟系統間發生聯系的時候。實現溝通結構經常扮演雙重角色,除了實現自身需求,還包含數據通信功能。這就給公司的成長和變化造成了很多問題,其影響會在下一節中進行評估。
圖 1-5:臨時性的數據溝通結構的例子
1.3.4 康威定律和溝通結構
設計系統的組織……受到約束,產生的設計是這些組織的溝通結構的副本。
—— Melvin Conway,“How Do Committees Invent?”(1968 年 4 月)
這句話被稱為康威定律,它表明一個團隊將根據其組織的溝通結構來構建產品。業務溝通結構將人員組織成團隊,這些團隊通常生產由他們的團隊邊界限定的產品。實現溝通結構提供了對給定產品的子領域數據模型的訪問,但是較弱的數據通信能力限制了其對其他產品的訪問。
因為領域概念跨越了不同業務,所以同一個組織里的不同界限上下文之間通常需要對方的領域數據。實現溝通結構一般不擅長提供這種通信機制,雖然它們在滿足自身界限上下文的需求時表現得很出色。它們會以兩種方式來影響產品設計。首先,由于在整個組織內傳遞所需的領域數據很低效,它們不鼓勵創建邏輯獨立的新產品。其次,它們會提供能輕松訪問現有領域數據的方式,但存在要不斷擴展領域以滿足新業務需求的風險。這種特殊的模式體現在單體設計中。
數據溝通結構在組織設計和構建產品的過程中扮演著關鍵角色,但對于許多組織來說這種結構是長期缺失的。如前所述,實現溝通結構除了自身的角色外,還經常扮演數據溝通結構的角色。
一些組織試圖通過其他實現方式來提升訪問領域數據的能力,但這些努力有其自身的缺點。例如,盡管共享數據庫的做法會促進反模式并且通常無法進行充分擴展以滿足所有的性能要求,但還是經常被使用。數據庫方案也許僅僅提供了一個只讀副本,但是這可能會不必要地暴露其內部數據模型。批處理程序能夠將數據轉存到文件中以被其他程序讀取,但這種方法會造成數據一致性和多信息源的問題。最后,所有這些解決方案都會導致實現之間的緊耦合,并進一步將系統架構強化為點對點的關系。
如果你發現在組織中訪問數據很困難,或者由于所有數據都存在于單獨的實現中,你的產品不得不不斷擴大其涵蓋的范圍,那你可能正受到糟糕的數據溝通結構的影響。這個問題會隨著組織的發展、新產品的開發以及對訪問通用領域數據需求的增加而進一步擴大。
1.4 傳統計算中的溝通結構
一個組織的溝通結構極大地影響了工程實現的創建方式。在團隊的層面也是如此:團隊的溝通結構影響了其為所負責的特定業務需求所構建的解決方案。下面來看看具體實踐。
考慮這樣的場景:有一個團隊負責一個單獨的服務及支持該服務的單獨的數據庫。團隊成員很樂于提供他們的業務功能,一切都很美好。有一天團隊領導帶來了一個新的業務需求。這個需求跟該團隊正在做的業務是相關的,可能將其加到現有的服務里就可以。但是,它與當前服務的差異程度也足以讓團隊考慮去開發一個新服務。
團隊目前處在一個十字路口:是用新服務來實現新的業務需求,還是簡單地將其加到現有的服務中去?下面進一步看看他們的選擇。
1.4.1 選項1:創建一個新服務
如果業務需求與原服務差異足夠大,那么可以將其放入新的服務。但是數據呢?新的業務功能需要一些舊數據,但那些數據目前限定在現有的服務內。此外,團隊并沒有真正的啟動新的、完全獨立的服務的流程。另外,團隊越來越大,公司也在快速成長。如果團隊在將來不得不被拆分,那么擁有模塊化和獨立的系統可能會讓劃分職權更加容易。
這種方法存在一些風險。團隊必須找到一種從原有數據庫中復制合適的數據到新數據庫中的方法。他們需要確保不會暴露內部工作邏輯,并且對數據結構的變更不會影響任何其他復制了他們數據的團隊。此外,被復制的數據通常是過時的,因為他們只能每 30 分鐘實時同步一次生產數據,這樣才不會給數據庫帶來大量查詢,進而造成過載。復制數據的連接需要被監控和維護,以保證它的正確運行。
讓新服務啟動并運轉起來也有風險。他們需要管理兩套數據庫、兩個服務,并且要為它們建立日志記錄、監控、測試、部署和回滾流程。他們還必須注意同步任何數據結構的變更,以免影響相關的系統。
1.4.2 選項2:將它加入現有服務中
另一個選項是在現有服務里創建新的數據結構和業務邏輯。所需的數據已經在當前的數據庫中,日志記錄、監控、測試、部署和回滾流程也早已經被定義和使用。團隊很熟悉這個系統并且能正確地實現邏輯,他們的單體模式支持這種服務設計方法。
這種方法也有一些風險,盡管它們更不易被察覺。隨著變更的不斷發生,實現的邊界可能會變得模糊,特別是因為不同的模塊通常被捆綁在同一個代碼庫里。通過跨邊界和直接跨模塊耦合來快速添加特性實在太容易了。能夠快速迭代是一個很大的優勢,但這是以高耦合、低內聚和缺少模塊化為代價的。雖然團隊可以防范這種情況,但這需要出色的規劃和對邊界的嚴格遵守,而在面對緊張的排期、缺乏經驗和服務權責改變等情況時,這些要求往往會被擱置一邊。
1.4.3 兩種選項的利弊
大部分團隊會選擇第二個選項,即添加功能到現有的服務中。這個選擇并沒有錯,單體架構是很有用且強大的結構,能夠為企業帶來巨大的價值。第一個選項首先會遇到與傳統計算相關的兩個問題:
● 難以可靠地訪問另一個系統的數據,特別是在大規模訪問和實時訪問的情況下;
● 創建和管理新的服務會帶來很大的開銷和風險,特別是在組織沒有處理此類事情的既定方法的情況下。
訪問本地數據通常比訪問另一個數據存儲中的數據簡單。要獲取任何封裝在屬于其他團隊的數據存儲中的數據都是很難的,因為這需要跨越實現和業務溝通邊界。隨著數據、連接數和性能需求的增長,這會變得越來越難以維護和擴展。
雖然復制必要的數據是值得的,但并非萬無一失。這個模型鼓勵許多直接的點對點的耦合,而隨著組織的發展、業務單元和所有者的變化,以及產品的成熟和逐漸淘汰,這些耦合將變得難以維護。它創建了一種嚴格的技術依賴關系,即兩個團隊(存儲數據的團隊和復制數據的團隊)的實現溝通結構要求它們在更改數據時進行同步工作。這需要很大的投入來確保不會過度暴露一個實現的內部數據模型,以免其他系統與之緊密耦合。擴展性、性能和系統可用性通常是這兩個系統存在的問題,因為數據的復制查詢可能給數據源系統帶來不可持續的負載。在緊急事故發生之前,失敗的同步過程可能并不會被察覺。部落文化可能導致一個正在復制數據的團隊認為這就是原始數據。
被復制的數據在查詢完成和數據傳輸完成時總是有些過時的。數據集越大,來源越復雜,副本與數據源不同步的可能性就越大。當系統期望彼此擁有完美的、最實時的數據時,這會是個問題,尤其是在就這些數據進行相互通信時。例如,由于數據過時,一個報表服務可能會上報與計費服務不同的值。這會對服務質量、報表、分析和基于金錢的決策制定產生嚴重的傳導影響。
無法在整個公司內正確地傳播數據并非由于概念上的根本性缺陷。恰恰相反,這是由于數據溝通結構的弱化或缺失。在前面的場景中,團隊的實現溝通結構作為相當有限的數據溝通結構在執行著雙重任務。
事件驅動型微服務的一個原則是核心業務數據應該易于獲得,并且可以由任何需要它的服務使用。這將用一個正式的數據溝通結構替代此場景中的臨時數據溝通結構。對于這個假設的團隊,這種數據溝通結構能夠消除從其他系統獲取數據的大部分困難點。
1.4.4 團隊場景(續)
該團隊最終決定采用第二個選項,在同一服務內加入新特性。這種做法簡單便捷,并且從那之后他們已經實現了很多新特性。隨著公司的成長,團隊也在壯大,經過一年時間,是時候考慮將原團隊拆分為兩個更小、更聚焦的團隊了。
現在必須指定每個新團隊負責之前服務中的某些業務功能。每個團隊的業務需求都基于最需要關注的業務領域進行了極好的劃分。但是,要拆分實現溝通結構并不那么容易。就如之前的情況,似乎兩個團隊都需要大量相同的數據來滿足各自的需求。新的問題出現了。
● 哪個團隊應該擁有哪些數據?
● 數據應該存儲在哪里?
● 兩個團隊都需要修改值的數據怎么辦?
團隊領導們覺得最好繼續共享原有的服務,他們可以各自負責處理不同的部分。這需要更多的跨團隊溝通和同步工作,而這些工作可能會導致生產效率降低。并且,如果將來他們的規模再擴大一倍,又該怎么辦呢?或者,如果業務需求變化使得他們再也無法用相同的數據結構去滿足需求,又該如何?
1.4.5 沖突的壓力
原有的團隊中有兩個沖突的壓力。要保持所有數據在一個服務里面,以使添加新功能更加快捷和簡便,代價就是擴大實現溝通結構。最終,隨著團隊規模的擴大,需要拆分業務溝通結構——緊跟而來的是要給新的團隊重新分配業務需求。但是,實現溝通結構在當前模式下無法支持重新分配,它需要被分解成適當的組件。這兩種方法都沒有可伸縮性,并且都需要做一些不同的事情。這些問題都來自同一個根源:一種在實現溝通結構之間弱化的、不明確的數據通信方式。
1.5 事件驅動的溝通結構
事件驅動方法提供了一種替代傳統的實現行為和數據溝通結構的選項。基于事件的通信不是“請求–響應”通信的簡單替代品,而是服務之間完全不同的通信方式。一個事件流的數據溝通結構從數據訪問場景中解耦了數據的生產和所有。服務之間不再通過一個“請求–響應”API 發生聯系,而是通過在事件流里定義的事件數據(這個過程在第 3 章中有更詳細的描述)發生聯系。生產者的職責僅限于生產定義良好的數據到它們各自的事件流中。
1.5.1 事件是通信的基礎
所有可共享的數據都被發布到一系列事件流中,構成了一個連續、規范的敘述,詳細描述了組織中發生的所有事情。這成了系統間相互通信的渠道。大部分事物能作為事件進行通信,從簡單的某個事情的發生到復雜的、有狀態的記錄。事件就是數據。它們不僅僅是表明數據已經準備就緒的信號,也不僅僅是從一個實現向另一個實現直接進行數據傳輸的方法。相反,它們既是數據存儲又是服務間異步通信的一種方式。
1.5.2 事件流提供了單一事實來源
事件流里的每個事件都是事實的一個狀態,所有這些狀態合起來構成了單一事實來源——這是組織內所有系統相互通信的基礎。溝通結構的好壞取決于其信息的真實性,因此組織采用事件流敘述作為單一事實來源是至關重要的。如果有些團隊選擇在其他位置放置一份有沖突的數據,那么事件流作為組織數據通信主干的功能將顯著降低。
1.5.3 消費者執行自己的建模和查詢
基于事件的數據溝通結構不同于擴大化的實現溝通結構,因為它不能提供數據查詢和查找功能。所有的業務和應用邏輯必須被封裝在事件的生產者和消費者內部。
數據訪問和建模需求被完全轉移到了消費者一方,每個消費者需要從源事件流中獲取他們自己的事件副本。任何查詢復雜度也會從數據所有者的實現溝通結構轉移到消費者的實現溝通結構。消費者全權負責來自多個事件流的數據混合、特定的查詢功能,或者其他特定業務的實現邏輯。生產者和消費者都不必為了數據通信方式而提供查詢機制、數據傳輸機制、API(應用編程接口)和跨團隊的服務。它們現在的責任僅限于解決當前的界限上下文的需求。
1.5.4 整個組織的數據溝通得到改善
數據溝通結構的使用是一種逆轉,所有可共享的數據都暴露在了實現溝通結構外部。不是所有的數據都必須共享,因此也不是所有的數據都需要發布到一系列事件流中。但是,任何其他團隊或服務感興趣的數據都必須發布到公共事件流集合中,這樣數據的生產和所有就變得完全解耦了。這提供了系統架構中長期缺失的正式的數據溝通結構,并且更好地遵守了“高內聚,低耦合”的界限上下文原則。
應用程序現在可以訪問原本很難通過點到點連接獲得的數據了。新的服務可以簡單地從典型事件流中獲得所需的數據,創建它們自己的模型和狀態,并執行任何必要的業務功能,而無須依賴與其他服務的直接點到點連接或 API。這就釋放了組織在任何產品中更高效地使用海量數據的潛力,甚至可以以獨特而強大的方式整合來自不同產品的數據。
1.5.5 高可訪問的數據利于業務變更
事件流包含對業務操作至關重要的核心領域事件。雖然各團隊可能重組,項目也會一個接一個地開展,但是重要的核心領域數據可以隨時提供給任何有需要的新產品,這個能力獨立于任何具體的實現溝通結構。這為業務提供了空前的靈活性,因為訪問核心領域事件不再依賴于任何具體的實現。
1.6 異步的事件驅動型微服務
事件驅動型微服務支持業務邏輯轉換和操作以滿足界限上下文的需求。這些應用的任務是滿足這些需求并向其他下游消費者發布任何必要的事件。下面是使用事件驅動型微服務的主要優點。
顆粒化
服務可以恰當地映射界限上下文,并且在業務需求發生變化時可被輕易重寫。
可伸縮性
單獨的服務可以根據需要進行擴縮容。
技術靈活性
服務可以使用最合適的語言和技術,也可以使用最前沿的技術進行簡單的原型開發。
業務需求靈活性
顆粒化微服務的所有權很容易進行重組。與大型服務相比,它們有更少的跨團隊依賴,并且組織可以快速響應業務需求的變化,否則這些變化會受到數據訪問障礙的阻礙。
松耦合
事件驅動型微服務耦合于領域數據而不是特定的實現 API。數據 schema 能極大地改進管理數據變更的方式,這部分內容會在第 3 章中詳細介紹。
持續交付支持
發布一個小型的、模塊化的微服務很容易,并且可在需要時將其回滾。
高可測試性
微服務的依賴比大型單體服務少,因此可以更容易地模擬所需的測試端點并保證適當的代碼覆蓋率。
使用事件驅動型微服務的示例團隊
現在回到前面那個團隊早期做決策的時候,這次他們使用的是事件驅動的數據溝通結構。
一個新的業務需求被提交給了這個團隊。此需求與該團隊當前負責的產品相關,但它們的差異程度也足以將其實現到單獨的一個服務里。將其添加到當前的服務是否違反了單一權責原則且擴大了當前所定義的界限上下文?或者它就是對現有服務的一次簡單擴展,可能添加了一些新的相關數據和功能?
之前所說的技術問題,比如確定從哪里獲取數據和如何接收數據、批處理的同步,以及需要實現同步 API 等,在采用事件驅動型微服務后都基本被消除了。該團隊可以啟動一個新的微服務并從事件流中獲取必要的數據。如果需要,可以一直回溯到事件流中最開始時的數據。團隊完全可以整合用于其他服務的公共數據,只要這些數據只用于實現新的界限上下文需求。這些數據的存儲和結構完全由該團隊來決定,他們可以選擇保留哪些字段和拋棄哪些字段。
業務風險也得到了緩解,因為小型的、細粒度的服務允許由單一團隊來負責,團隊可以根據需要進行規模調整和重組。當團隊變得太大而無法在單一業務所有者下進行管理時,可以根據需要拆分并重新分配微服務的權責。事件數據的所有權跟著生產事件的服務移動,并且所做出的組織決策會減少將來執行任務時所需的跨團隊通信。
如果創建新服務和獲取所需數據的開銷很小,那么微服務的特性就防止了意大利面式代碼和擴大化的單體應用的出現。可伸縮性的關注點現在聚焦于單個事件處理服務,這些服務可以根據需要變更 CPU、內存、磁盤和實例計數的規模。剩余的伸縮需求被轉移到了數據溝通結構上,數據溝通結構必須確保能夠處理服務從事件流中消費和往事件流生產事件所產生的各種負載。
然而,要實現這一切,團隊需要確保數據確實存在于數據溝通結構中,并且他們必須有輕松啟動和管理一系列微服務的方法。這就需要整個組織接受事件驅動型微服務架構。
1.7 同步式微服務
微服務可以通過事件(本書所建議的方法)實現成異步的形式,或者實現成同步的形式(同步服務通常出現在面向服務的架構中)。同步的微服務通常采用“請求–響應”的方法來實現,服務間通信直接通過 API 來滿足業務需求。
1.7.1 同步式微服務的缺點
同步式微服務有很多問題,這使得它們難以大規模地使用。這并不是說一個公司不能用同步式微服務來獲得成功,Netflix、Lyft、Uber 和 Facebook 等公司的成功就證明了這一點。但是也有許多公司使用過時的、混亂的意大利面式代碼和單體應用發了大財,所以不要把公司的最終成功與底層架構的質量混為一談。市面上有許多介紹如何實現同步式微服務的圖書,可以讀讀它們以更好地理解同步方法。2
2例如由 Sam Newman 所著的《微服務設計》以及由 Kasun Indrasiri 和 Prabath Siriwardena 合著的 Microservices for the Enterprise。
此外,請注意,無論是點到點的“請求–響應”型微服務還是異步的事件驅動型微服務,嚴格來說都沒有絕對的優劣之分。在一個組織里它們都有自己的一席之地,因為有些任務更適合采用其中的一種模式。然而,我以及我的許多同行和同事的經驗表明,事件驅動型微服務架構提供了無與倫比的靈活性,這是同步的“請求–響應”型微服務所不具備的。當你繼續閱讀本書時可能會同意我的觀點,至少你會了解它們的優缺點。
以下是同步的“請求–響應”型微服務的幾大缺點。
點到點的耦合
同步的微服務依賴其他服務來幫助它們執行業務任務。那些服務同樣有自己的依賴服務,而這些依賴服務又有自己的依賴服務,以此類推。這會導致過度的扇出,且難以跟蹤哪些服務要為實現業務邏輯的特定部分負責。服務之間的連接數量多得驚人,這會進一步增強現有的溝通結構并使得未來的變更更加困難。
有依賴的可伸縮性
你自身服務的擴容能力依賴于所有依賴服務的擴容能力,并且直接與通信的扇出程度有關。實現技術可能是可伸縮性的瓶頸。高度變化的負載和激增的請求模式會使情況更加復雜,所有這些都需要在整個體系架構內同步處理。
服務故障的處理
如果一個依賴服務關閉,則必須做出如何處理這個異常的決策。生態系統中的微服務越多,決定如何處理停機、何時重試、何時失敗以及何時恢復以確保數據一致性就越困難。
API 版本和依賴管理
多個 API 定義和服務版本通常需要同時存在。強制讓客戶端升級到最新的 API 并不總是可行或可取的。這會增加跨多個服務協調 API 更改請求的復雜性,特別是當它們伴隨著對底層數據結構的更改時。
數據訪問耦合于實現
同步的微服務在訪問外部數據時會遇到所有跟傳統的服務相同的問題。雖然有減少訪問外部數據需求的服務設計策略,但微服務通常還是需要訪問來自其他服務的通用數據。這就把數據訪問和可伸縮性的責任重新放在了實現溝通結構上。
分布式的單體應用
服務可以被組合成一個分布式的單體應用,在它們之間進行許多相互交織的調用。這種情況通常出現在團隊分解一個單體應用,并決定使用同步的點到點調用來模擬這個單體應用內部的邊界時。點對點的服務可以很容易地模糊界限上下文的邊界,因為對遠程系統的函數調用可以一行一行地插入現有單體式代碼。
測試
集成測試可能很困難,因為每個服務都需要完全可操作的依賴項,而這些依賴項又需要自己的依賴項。在單元測試中將它們截取出來可能是可行的,但并不足以滿足更廣泛的測試需求。
1.7.2 同步式微服務的優點
同步式微服務有許多不可否認的優點。一些數據訪問模式很適合直接的“請求–響應”耦合,比如驗證用戶身份和 AB 測試中的上報。與外部第三方解決方案的集成大多數時候使用同步機制,并且通常使用 HTTP 以提供一種靈活的、與語言無關的通信機制。
跟蹤跨多個系統的操作在同步環境下會比異步環境下更簡單。詳細的日志可以顯示在哪些系統上調用了哪些函數,從而實現業務操作的高度可調試性和可見性。
承載 Web 和移動體驗的服務通常由“請求–響應”設計提供支持,無論它們是同步還是異步性質。客戶會收到完全滿足了其需求的及時響應。
經驗因素也是非常重要的,尤其是當今市場上的許多開發者往往對同步、單體類型的編碼更有經驗。總的來說,這使得獲取熟悉同步系統的人才比獲取熟悉異步事件驅動開發的人才更容易。
一個公司的架構很少完全基于事件驅動型微服務。混合式架構肯定會成為常態,同步和異步的解決方案會根據問題空間的需要進行并行部署。
1.8 小結
溝通結構指導著在組織的整個生命周期中如何創建和管理軟件。數據溝通結構通常是開發不完全且臨時的,但是,如事件驅動系統所體現的那樣,引入一組持久的、易于訪問的領域事件可以使得更小的、專門構建的實現得以應用。
- Java語言程序設計
- Node.js 10實戰
- Microsoft Application Virtualization Cookbook
- 實用防銹油配方與制備200例
- Building Mapping Applications with QGIS
- Mastering Apache Spark 2.x(Second Edition)
- 自制編程語言
- Getting Started with Gulp
- Visual Basic程序設計上機實驗教程
- Multithreading in C# 5.0 Cookbook
- ElasticSearch Cookbook(Second Edition)
- 網絡數據采集技術:Java網絡爬蟲實戰
- AMP:Building Accelerated Mobile Pages
- 3D Printing Designs:Octopus Pencil Holder
- 算法精解:C語言描述