UBer面向領域的微服務體系架構實踐

 

介紹

最近,人們對面向服務的系統架構和微服務系統架構的缺點進行了大量的討論。儘管僅僅在幾年前,由於微服務體系架構提供了許多好處,如獨立部署的靈活性、明確的所有權、提高系統穩定性以及更好地分離關注點等,但近年來,人們開始譴責微服務的傾向極大地增加了複雜性,有時甚至使微小的特性也難以構建。

隨着 Uber 已經發展到大約 2200 個關鍵微服務,我們親身體驗了這些折衷。在過去兩年裏,Uber 試圖降低微服務的複雜性,同時仍然保持微服務架構的優勢。通過這篇博文,我們希望介紹我們對微服務體系結構的通用方法,我們稱之爲“面向領域的微服務體系架構”(Domain Oriented microservice Architecture,DOMA)。

由於這些缺點,近年來批評微服務架構變的很流行,但很少有人主張徹底拒絕微服務架構。系統的運營效益(可維護性)太重要了,而且似乎沒有或極其有限的替代方案。我們使用 DOMA 的目標是爲那些希望在保持與微服務體系結構相關的靈活性的同時降低總體系統複雜性的組織提供一條前進的道路。

這篇文章解釋了 DOMA,導致 Uber 採用這種架構的原因,它對平臺和產品團隊的好處,最後,爲希望採用這種架構的團隊提供了一些建議。

什麼是微服務

微服務是面向服務體系架構的擴展。與 2000 年代相當大的“服務”不同,微服務是代表一組範圍狹窄的功能的應用程序。這些應用程序在網絡上託管和可用,並公開定義良好的接口。其他應用程序通過進行“遠程過程調用”(RPC)來調用此接口。

微服務體系結構的關鍵特徵是代碼的託管、調用和部署方式。如果我們考慮大型的、單片的應用程序,它們通常被分成具有定義良好的接口的封裝組件。這些接口將直接在進程內調用,而不是通過網絡調用。通過這種方式,我們可以開始將微服務視爲一個性能受到影響的庫(網絡 I/O 和序列化/反序列化),以便調用它的任何函數。

當我們以這種方式考慮微服務時,我們可能會質疑爲什麼要採用微服務架構。答案通常是獨立部署和擴展。對於大型的單片應用程序,組織必須一次部署或發佈所有代碼。應用程序的每個新版本都可能涉及到許多更改。部署變得有風險而且耗時。任何人都可以搞垮整個系統。

換言之,組織採用微服務是爲了獲得運營效益而犧牲性能。組織還必須承擔維護支持微服務所需的基礎設施的成本。事實證明,在許多情況下,這種權衡是有意義的,但它也是反對過早採用微服務體系結構的有力理由。

動機

在 Uber,我們採用了微服務架構,因爲我們(大約在 2012-2013 年)主要有兩個單一服務,並遇到了微服務解決的許多運營問題:

  • 可用性風險高。在一個單一的代碼庫中進行一次迴歸可以使整個系統(在本例中是 Uber 的所有部分)崩潰。
  • 部署高風險高成本。在頻繁需要回滾的情況下執行這些操作既痛苦又耗時。
  • 關注點分離差。用龐大的代碼庫很難保持關注點的良好分離。在指數增長的環境中,權宜之計有時會導致邏輯和組件之間的邊界差。
  • 執行效率低。這些問題的結合使得團隊很難自主或獨立地執行任務。

換言之,隨着 Uber 從 10 歲的工程師成長到 100 歲的工程師,多個團隊擁有技術堆棧的一部分,這種單一的架構將團隊的命運捆綁在一起,使得獨立運營變得困難。

因此,我們採用了微服務架構。最終,我們的系統變得更加靈活,這使得團隊更加自主。

  • 系統可靠性。在微服務體系結構中,系統的整體可靠性提高了。單個服務可以停止(並回滾),而不必關閉整個系統。
  • 關注點分離。從體系結構上講,微服務體系結構迫使您提出這樣一個問題:“爲什麼存在這種服務?“更清楚地定義不同組成部分的作用。
  • 所有權明確。誰擁有什麼代碼變得更加清楚了。服務通常在個人、團隊或組織級別擁有,以實現更快的增長。
  • 自主執行強。獨立部署+更清晰的所有權線將不同產品和平臺團隊的自主執行分離解鎖。
  • 開發迭代快。團隊可以獨立地部署他們的代碼,這使他們能夠以自己的速度執行。

毫不誇張地說,如果沒有微服務架構,Uber 將無法實現我們今天保持的規模和質量。

然而,隨着公司規模的擴大,從 100 名工程師到 1000 名工程師,我們開始注意到一系列與大大增加的系統複雜性相關的問題。在微服務體系結構中,人們用一個單片代碼庫來交換黑盒,黑盒的功能隨時可能改變,很容易導致意外行爲。

例如,爲了調查問題的根本原因,工程師們不得不在 12 個不同的團隊中處理大約 50 個服務。

理解服務之間的依賴關係會變得相當困難,因爲服務之間的調用可以深入到許多層。第 n 個依賴項中的延遲峯值可能會導致上游問題的級聯。如果沒有正確的工具,就不可能看到實際發生的情況,這使得調試變得困難。

 

 

(優步微服務架構(Microservice Architecture),約在 2018 年年中由 Jaeger 提供)

爲了構建一個簡單的特性,工程師通常必須跨多個服務工作,這些服務都由不同的個人和團隊擁有。這就需要在會議、設計和代碼評審方面進行廣泛的協作。當團隊在彼此的服務中構建代碼、修改彼此的數據模型,甚至代表服務所有者執行部署時,早期明確的服務所有權的承諾將受到損害。可以形成網絡化的整體,其中看起來獨立的服務都必須部署在一起才能安全地執行任何更改。

 

 

(Uber 在 2018 年左右的一個複雜流程示例,在 DOMA 之前,需要 10 個接觸點進行簡單集成。)

結果是開發人員體驗變慢,服務所有者不穩定,遷移更痛苦等等。對於已經採用微服務架構的組織來說,沒有回頭路可走。這就成了“不能和他們一起生活,不能沒有他們就不能生活”的例子。

面向領域的微服務體系架構(DOMA)

如果我們可以把微服務看作是 I/O 綁定的庫,把“微服務體系架構”看作一個大型的、分佈式的應用程序,那麼我們就可以使用理解良好的體系結構來思考如何組織代碼。

因此,“面向領域的微服務體系架構”大量借鑑了組織代碼的既定方法,如領域驅動設計、乾淨的體系架構、面向服務的體系架構以及面向對象和接口的設計模式。我們認爲 DOMA 是創新的,因爲它是一種相對新穎的方法,可以在大型組織中的大型分佈式系統中利用既定的設計原則。

與 DOMA 相關的核心原則和術語如下:

  • 我們不是以單個微服務爲導向,而是以相關微服務的集合爲導向。我們稱之爲域。

  • 我們進一步創建我們稱之爲層的域集合。域所屬的層建立了允許該域中的微服務承擔的依賴關係。我們稱之爲層設計。

  • 我們爲域提供乾淨的接口,這些域被視爲集合的單入口點。我們稱之爲“門”。

  • 最後,我們確定每個域對其他域應該是不可知的,也就是說,一個域不應該在其代碼庫或數據模型中有與另一個域硬編碼相關的邏輯。由於團隊經常需要在另一個團隊的域中包含邏輯(例如,自定義驗證邏輯或數據模型上的一些元上下文),我們提供了一個擴展架構來支持域中定義良好的擴展點。

換句話說,通過提供一個系統化的體系結構、域網關和預定義的擴展點,DOMA 打算將微服務體系結構從複雜的東西轉換成可以理解的東西:一組靈活、可重用和分層的組件。

本文的其餘部分將深入探討 Uber 對 DOMA 的實現、我們所看到的好處,以及對可能希望採用這種方法的公司的實用建議。

Uber 的實踐

領域

Uber 域表示一個或多個微服務的集合,這些服務與功能的邏輯分組相關聯。在設計域時,一個常見的問題是“域應該有多大?”?“我們這裏沒有指導。有些域可以包含幾十個服務,有些域只有一個服務。重要的任務是仔細考慮每個集合的邏輯角色。例如,我們的地圖搜索服務構成一個域,票價服務是一個域,匹配平臺(匹配車手和司機)是一個域。這些也不總是遵循公司的組織結構。Uber 地圖組織本身分爲三個域,在 3 個不同的網關後面有 80 個微服務。

分層設計

分層設計回答了“什麼服務可以調用什麼其他服務”的問題?“在 Uber 的微服務架構中。因此,我們可以將層設計視爲“大規模的關注點分離”,或者,我們可以將層設計視爲“規模上的依賴管理”

分層設計描述了一種機制,用於考慮 Uber 服務依賴性中的故障爆炸半徑和產品特定性。當域從底層移動到頂層時,它們在中斷情況下影響的服務更少,並且代表更具體的產品用例。相反,底層的功能有更多的依賴項,因此往往具有更大的爆炸半徑,並代表更一般的業務功能集。下圖說明了這一概念。

 

 

我們可以將頂層視爲特定的用戶體驗(如移動功能),將底層視爲通用的業務功能(如帳戶管理或市場旅行)。層只依賴於它們下面的層,這給我們提供了一個有用的啓發來考慮像爆炸半徑和域集成這樣的問題。

值得注意的是,功能通常會將圖表從特定的“向下”移動到更一般的位置。可以想象,隨着需求的發展,一個簡單的特性最終會越來越成爲一個平臺。事實上,這種向下遷移是意料之中的,Uber 的許多核心業務平臺一開始都是針對騎手或司機的功能,隨着我們開發更多的業務線,這些功能變得更加普遍(比如 Uber Eats 或 Uber Freight)。

在 Uber 內部,我們建立了以下五個層次:

  • 基礎設施層。提供任何工程組織都可以使用的功能。這是 Uber 對存儲或網絡等重大工程問題的解答。

  • 業務層。提供 Uber 作爲一個組織可以使用的功能,但這些功能並不特定於特定的產品類別或業務線(LOB),如乘車、就餐或貨運。

  • 產品層。提供與特定產品類別或 LOB 相關的功能,但對移動應用程序是不可知的,例如多個面向騎乘的應用程序(Rider、Rider“Lite”、m。優步網等等)。

  • 表現層。提供與面向消費者的應用程序(移動/網絡)中存在的功能直接相關的功能。

  • 邊緣層。向外部世界安全地提供 Uber 服務。這一層也是移動應用感知的。

如您所見,每個後續層代表一組越來越具體的功能,並且具有越來越小的爆炸半徑(或者,換句話說,越來越少的組件依賴於該層中的功能)。

網關

術語“網關 API”在微服務架構中已經是一個廣泛確立的概念。我們的定義與已建立的定義差別不大,只是我們傾向於將網關視爲底層服務集合的單一入口點,我們稱之爲域。網關的成功依賴於 API 設計的成功。

 

 

上圖說明了網關的高級關係圖。它抽象了域的內部細節-多個服務、數據表、ETL 管道等。只有接口-rpcapis、消息傳遞事件和查詢才公開給其他域。

由於上游消費者只對單個服務進行操作,因此網關在未來的遷移、可發現性和系統複雜性的總體降低方面提供了許多好處,因爲上游服務只採用一個依賴項,而不是依賴於一個域中可能存在的多個下游服務。如果我們從面向對象設計的角度來考慮網關,它們就是接口定義,它使我們能夠在底層“實現”(在本例中是底層微服務的集合)方面做任何我們想做的事情。

擴展

擴展表示一種擴展域的機制。擴展的基本定義是,它提供了一種機制來擴展底層服務的功能,而不會更改該服務的實際實現,也不會影響其整體可靠性。在 Uber,我們提供兩種不同的擴展模型:邏輯擴展和數據擴展。擴展的概念允許我們將架構擴展到多個能夠獨立工作的團隊。

邏輯擴展

邏輯擴展提供了一種擴展服務底層邏輯的機制。對於邏輯擴展,我們使用提供程序或插件模式的變體,接口是按服務定義的。這使得擴展團隊能夠以接口驅動的方式實現擴展邏輯,而無需修改底層平臺的核心代碼。

例如,一個司機上網。通常,我們會進行各種檢查,以確保司機可以上網(安全檢查、合規性檢查等)。每一個都由一個單獨的團隊擁有。實現這一點的一種方法是讓每個團隊在同一個端點中編寫邏輯,但這會帶來複雜性。每次檢查都需要定製的、完全無關的邏輯。

在邏輯擴展的情況下,“聯機”端點將定義一個接口,它們希望每個擴展都符合預定義的請求類型和響應。每個團隊將註冊一個負責執行此邏輯的擴展。在這種情況下,他們可以簡單地獲取驅動程序的一些上下文並返回一個 bool,說明驅動程序是否可以聯機。go-online 端點將簡單地遍歷這些響應,並確定其中是否有任何響應爲 false。

這將核心代碼與每個擴展解耦,並在擴展之間提供隔離,擴展不知道其他邏輯在執行什麼。圍繞這一點很容易建立更多的功能,比如可觀測性或特徵標記。

數據擴展

數據擴展提供了一種將任意數據附加到接口的機制,以避免核心平臺數據模型膨脹。對於數據擴展,我們利用 Protobuf 的任何功能,以便團隊可以向請求添加任意數據。服務通常會存儲這些數據或將其傳遞給邏輯擴展,這樣核心平臺就不會負責反序列化(從而“瞭解”)這個任意上下文。Protobuf 的任何實現都會帶來一些基礎設施開銷,以換取更強的類型。爲了更簡單的實現,可以很容易地使用 JSON 字符串來表示任意數據。

 

 

定製

除了邏輯和數據擴展之外,Uber 的許多團隊已經引入了適合自己領域的擴展模式。例如,與我們的表示體系結構相關的許多集成都使用基於 DAG 的任務執行邏輯。

好處效益

Uber 幾乎每個主要領域都受到 DOMA 的某種程度的影響。在過去的一年裏,我們主要關注 Uber 的業務層,它爲我們的各個業務線提供了通用邏輯。

DOMA 在 Uber 還很年輕,我們很高興將來能分享更多的數據和深入的架構示例。然而,在簡化開發人員體驗和降低總體系統複雜性方面,早期跡象是非常積極的。

產品和平臺

DOMA 是 Uber 產品和平臺團隊一致努力的結果。平臺支持成本通常下降一個數量級。產品團隊從護欄和加速開發中獲益。

例如,我們的擴展體系結構的早期平臺消費者能夠通過採用一種擴展體系結構,將劃分優先級和集成新功能的時間從三天縮短到三小時,從而縮短了消費者的代碼審查、規劃和學習曲線時間。

降低複雜性

以前,產品團隊需要調用許多下游服務來利用一個域;現在他們只需要調用一個。通過減少搭載新功能的接觸點數量,平臺能夠將登錄時間減少 25-50%。此外,我們能夠將 2200 個微服務分爲 70 個域。其中大約有 50%已經實施,而且大多數都有一些未來採用的計劃。

未來的遷移

在 Uber,我們計算出微服務的半衰期是 1.5 年,也就是說,每 1.5 年,我們的微服務就有 50%流失。沒有網關,微服務體系結構很容易因爲這種混亂而陷入“遷移地獄”。不斷變化的微服務不斷需要上游遷移。網關使團隊能夠避免對底層域服務的依賴,這意味着這些服務可以在不強制進行上游遷移的情況下進行更改。

去年 Uber 最大的兩次平臺重寫都發生在 gateways 之後。這些平臺有數百個依賴於它們的服務,這些服務必須遷移現有的消費者。在這些情況下,遷移的成本將非常高,使得完全重寫平臺變得不可行。

新的業務線和產品

使用 DOMA 設計的平臺已經被證明具有更高的可擴展性和更易於維護。大多數採用 DOMA 的 Uber 團隊之所以這麼做,是因爲支持新的業務線變得過於昂貴。

實用建議

本節爲可能希望採用 DOMA 的公司提供了一些實用的建議。這裏的指導原則是,在我們的經驗中,一個成熟和深思熟慮的微服務架構源於在正確的時間朝着正確的方向輕輕推動。事實是,對於一個人的整個微服務架構來說,真正的“重寫”是不可能的。

因此,我們認爲演化微服務架構更像是“修剪樹籬”,以便它最終正確增長,而不是自上而下或一次性的架構(或重新架構)工作。這是一個動態的、漸進的過程。

創業公司

驅動問題應該是“我們什麼時候應該採用微服務架構?“這對我們的組織有意義嗎?”?“正如我們在上面看到的,雖然微服務爲擁有大量工程師的組織帶來了運營上的好處,但這會帶來複雜性的增加,從而使功能更難構建。

在小型組織中,運營收益可能無法抵消架構複雜性的增加。此外,微服務體系結構通常需要專用的工程資源來支持,這可能超出了早期公司的預算,或者從優先級的角度來看是次優的。

考慮到這一點,將微服務完全推遲一段時間也不無道理。如果一個組織選擇採用微服務,它應該考慮“微服務作爲大型分佈式應用程序”的類比,以及它希望構建的微服務之間的關注點分離。另外,要認識到,第一批微服務很可能是最重要、持續時間最長的,因爲它們真實地描述了業務的核心。

中型公司

一旦一家公司成爲擁有多個團隊的中型企業,並且不同功能和平臺之間的關注點的明確分離變得模糊,微服務架構就變得更加有用。

在這個階段,人們可以開始考慮微服務之間的層次結構。依賴性管理可能變得更加重要,因爲一些服務開始變得對業務運營更加重要,並且越來越多的團隊依賴它們。

對平臺化的早期投資可能會帶來回報。如果一個人能夠創建完全不依賴產品的業務平臺,並避免核心平臺服務中的任意產品邏輯,那麼就有可能避免技術債務。在這一點上採用擴展來實現這個目標可能是有意義的。

鑑於微服務的數量可能仍然很低,將它們聚集在一起可能沒有意義。然而,這裏值得注意的是,在 Uber 的 DOMA 實現環境中,一個域可以包含一個服務,因此以“面向域”的方式思考可能仍然有用。

大型公司

更大的工程組織可能有數百名工程師和微服務以及幾個依賴關係。在這一點上,DOMA 達到了它的全部用途。很可能會有明顯的微服務集羣,這些集羣可以很容易地組合到具有網關的域中。遺留服務通常開始需要重構或重寫,然後再進行遷移,這意味着如果網關已經就位,它們將很快開始提供易於遷移的價值。

清晰的層次結構也將變得越來越重要,一些服務作爲針對特定功能或功能分組的“產品”服務運行,而其他服務將越來越多地支持多個產品並被視爲“平臺”。在現階段,保持任意產品邏輯與平臺的解耦是至關重要的,從而避免了平臺團隊的沉重運營負擔和全系統的不穩定。

最後的想法

隨着 Uber 越來越多的團隊開始採用 DOMA,我們仍在積極發展 DOMA。DOMA 的關鍵觀點是,微服務體系結構實際上只是一個大型的分佈式程序,您可以將同樣的原則應用到它的發展過程中,就像應用於任何軟件一樣。DOMA 只是在實踐中思考這些原則的一種方法。我們希望其他人覺得有用,我們期待反饋!

DOMA 本身就是跨職能部門努力的結果,Uber 的每個組織都有近 60 名工程師參與其中。

致謝

這項工作帶來了業界現有的多種設計模式來解決 Uber 的問題,同時也提出了一些新的模式,比如擴展。我們感謝業界在這方面所做的努力。我們也要感謝 Linkedin 的工程師們,他們爲我們講述了他們的經歷。

作者

亞當·格魯克

亞當·格魯克是優步的高級軟件工程師。他在優步的前 3.5 年裏,充實了我們的司機平臺團隊,並幫助擴大了我們的司機產品。最近,他還是 Uber 工程戰略團隊的一員,專注於高層系統架構和 Uber 平臺化工作。

原文: https://eng.uber.com/microservice-architecture/

 

 

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