面向領域的微服務架構

前言

近來,一些關於面向服務架構的話題,特別是針對微服務架構的弊端這個話題上進行了大量的討論。雖然在幾年前,微服務架構受到不少人的青睞,由於它們提供了許多好處,如獨立部署的靈活性、明確的全部權、系統穩定性的改善以及更好的分離問題等優勢。可是不久,就開始有人吐槽微服務會大幅增長系統複雜性,有時甚至連一些簡單的功能都難以構建。設計模式

隨着Uber發展,咱們目前擁有了大約2200個關鍵的微服務,而且也親身經歷了這些權衡。在過去的兩年裏,Uber一直在試圖下降微服務的複雜性的同時仍然保持着微服務架構的優點。咱們但願經過這篇博文介紹咱們對微服務架構的通用方法,咱們稱之爲 "面向領域的微服務架構"(DOMA)。安全

因爲這些缺點,近年來也有一些批評微服務架構的聲音,可是卻不多有人主張完全拒絕微服務架構。運營收益過重要了,並且彷佛沒有有效的替代方案。咱們介紹DOMA的目的是爲了給那些但願下降總體系統複雜性,同時又保持微服務架構相關靈活性的組織提供一些經驗建議。微信

這篇文章主要解釋了什麼是DOMA,以及Uber採用這種架構的緣由,它對平臺和產品團隊帶來哪些好處。最後,給想要採用這種架構的團隊一些建議。網絡

什麼是微服務

微服務是面向服務架構的延伸。與2000年代至關大的 "服務 "相比,微服務是表明一組小型功能的應用程序。這些應用經過網絡託管,並暴露出一個明肯定義接口。其餘應用程序經過進行遠程過程調用(RPC)方式來調用這個接口。架構

微服務架構的特色是代碼的託管、調用和部署方式。好比大型的單體應用,它們一般會被分割成具備明肯定義接口的封裝組件。而後,這些接口就能夠直接在進程中調用,而不是經過網絡。經過這種方法,咱們將微服務看做一個性能受到影響的庫(網絡I/O和序列化/反序列化),以便調用它的任何功能。分佈式

當咱們以這種方式來思考微服務時,可能會質疑爲何咱們會採用微服務架構。答案一般是獨立部署和擴展。對於一個大型的單體應用程序,應用被迫一次性部署或發佈全部的代碼。應用程序的每個新版本均可能涉及許多更改。部署變得風險大、耗時長。任何人均可以使整個系統癱瘓。ide

換句話說,業務採用微服務是以犧牲性能爲代價來獲取運營利益。業務還必須承擔維護支持微服務所需的基礎設施的成本。事實證實,在不少狀況下,這種權衡是頗有意義的,但這也是反對過早採用微服務架構的有力論據。微服務

動機

在Uber,咱們也採用了微服務架構,由於咱們(大約在2012-2013年)主要有兩個單體服務,遇到了不少經過微服務來解決的運營問題。工具

  • 可用性風險。單體代碼庫內的一次回滾就會使整個系統(在本例中是Uber的所有)癱瘓。性能

  • 部署風險大,成本高。在須要頻繁回滾的狀況下,執行這些操做既困難又耗時。

  • 不平滑的關注點分離。在龐大的代碼庫中,很難保持良好的關注點分離。尤爲在一個指數級增加的環境中,權宜之計有時會致使邏輯和組件之間的界限不清。

  • 執行效率低下。這些問題加在一塊兒,使得團隊很難獨立自主地執行任務。

隨着Uber從10多個工程師發展到100多個工程師,多個團隊擁有技術棧的碎片時,這種單一的架構將團隊的命運捆綁在一塊兒,使得獨立運做變得困難。

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

  • 系統的可靠性。在微服務架構中,總體系統的可靠性上升。單個服務能夠在不影響整個系統的狀況下宕機(並被回滾)。

  • 關注點的分離。從架構上來看,微服務架構迫使你去問 "這個服務爲何存在?"更加清晰地定義不一樣組件的角色。

  • 明確全部權。代碼擁有者變得更加清楚。服務一般由我的、團隊或組織級別擁有,從而實現更快的增加。

  • 自主執行。獨立的部署 更清晰的所屬權限,讓不一樣的產品和平臺團隊可以自主執行。

  • 開發人員的速度。應用團隊能夠獨立部署他們的代碼,這使得他們可以按照本身的項目進度來執行

絕不誇張地說,若是沒有微服務架構,Uber不可能達到今天所保持的規模和執行質量。

然而,隨着公司規模的進一步擴大,從100多名工程師到1000多名工程師,咱們開始注意到一系列與系統複雜性大大增長的相關問題。在微服務架構下,人們用單一的總體代碼庫換取了黑盒,黑盒的功能隨時可能發生變化,很容易形成意外狀況。

例如,工程師們不得不經過12個不一樣團隊大約50個服務來調查問題的根本緣由。

理解服務之間的依賴關係可能會變得至關困難,由於服務之間的調用可能會深刻許多層。第n個依賴關係的延遲峯值可能會致使上游的一連串問題。若是沒有合適的工具,就不可能看到實際發生的狀況,這讓調試變得困難。

爲了構建一個簡單的功能,工程師每每須要跨多個服務工做,全部這些服務都由不一樣的我的和團隊所擁有。這就須要跨部門跨團隊的合做,在會議、設計和代碼審查上花費時間。因爲團隊在彼此的服務中構建代碼,修改彼此的數據模型,甚至表明服務全部者執行部署,早期對服務全部權的明確界限劃分受到了影響。網絡化的單體可能會造成,看似獨立的服務都必須一塊兒部署才能安全地執行任何變動。

這樣所帶來的結果就是開發進度變慢、服務所屬不穩定、遷移更困難等。對於已經採用微服務架構的企業來講,已經沒有回頭路了。這就變成了 "有了它們不能活,沒有它們不能活"。

面向領域的微服務架構

若是咱們能夠將微服務視爲I / O綁定的庫,而將「微服務架構」視爲大型的分佈式應用程序,則可使用衆所周知的架構來思考如何組織代碼。

所以,「面向領域的微服務體系結構」大量借鑑了組織代碼的既定方法,例如 域驅動設計 , 清晰架構 , 面向服務的體系架構 以及面向對象和麪向接口的設計模式。 咱們認爲DOMA僅是創新,由於它是在大型應用的分佈式系統中利用既定設計原則的相對新穎的方法。

DOMA相關的核心原理和術語以下:

  1. 圍繞相關微服務的集合,稱爲 

  2. 域的集合稱之爲層。域所屬的層肯定了容許該域內的微服務承擔什麼依賴性,稱爲 層設計

  3. 爲域提供接口,這些域被視爲集合的單個入口點,稱爲 網關

  4. 肯定每一個域都應該與其餘域不可知,一個域不該該具備與其代碼庫或數據模型內部硬編碼的另外一個域相關的邏輯。因爲團隊常常須要在另外一個團隊的域中包含邏輯(例如,自定義驗證邏輯或數據模型上的某些元上下文),所以咱們提供了一種 擴展架構 ,以支持該域中定義明確的擴展點。

經過提供系統的體系結構,域網關和預約義的擴展點,DOMA打算將微服務體系結構從複雜的東西轉變爲可理解的東西:結構化的一組靈活,可重用和分層的組件。

這篇文章的其他部分將深刻研究Uber在DOMA中的實施,咱們已經看到的好處以及爲可能但願採用這種方法的公司提供的實用建議。

Uber的措施

Uber域表明一個或多個與邏輯功能分組綁定的微服務的集合。設計域時常見的問題是「域應該有多大?」有些域能夠包含數十個服務,有些域只能包含單個服務。重要的任務是仔細考慮每一個集合的 邏輯 角色。例如,咱們的地圖搜索服務構成一個域,票價服務是一個域,匹配平臺(匹配騎手和駕駛員)是一個域。這些也不老是遵循公司的組織結構。Uber Maps組織自己分爲三個域,在三個不一樣的網關後面有80個微服務。

層設計

層設計回答了「什麼服務能夠調用其餘什麼服務?」的問題。在Uber的微服務架構中,咱們能夠將層設計視爲「規模化的關注點分離」,或者,咱們能夠將層設計視爲「規模化的依賴管理」。

層設計描述了一種機制,用於考慮Uber的故障影響範圍和跨服務依賴的產品特異性。 當域從底層移到頂層時,它們在中斷的狀況下會影響較少的服務,並表明更多特定的產品使用案例。 相反,底層的功能具備更多的依存關係,所以趨向於具備更大的影響半徑,並表明了更通用的業務功能集。下圖說明了此概念。

能夠將頂層視爲具體的用戶體驗(例如移動功能),將底層視爲通用的業務功能(例如賬戶管理或市場行程)。層僅取決於其下的層,這爲咱們提供了一種有用的啓發式方法,能夠思考影響範圍和區域集成等問題。

值得注意的是,功能常常會從這個圖表中 "向下 "移動,從具體到更廣泛。能夠想象,一個簡單的功能,隨着需求的變多,最終會變成愈來愈多的平臺。事實上,這種向下遷移是意料之中的,Uber的許多核心業務平臺一開始都是針對騎手或司機的功能,隨着咱們開發出更多的業務線,它們也有了更多的依賴性,就會變得愈來愈通用(好比Uber Eats或Uber Freight)。

在Uber內部,咱們創建瞭如下五個層次。

  1. 基礎設施層。 提供任何工程項目均可以使用的功能。這是Uber對諸如存儲或網絡等重大工程問題的處理。

  2. 業務層。 提供應用可使用的Uber功能,但並不是特定於特定產品類別或業務線(LOB)的功能,例如乘車,進餐或貨運。

  3. 產品層。 提供與特定產品類別或LOB相關但與移動應用程序無關的功能,例如由多個面向應用程序的Rides所利用的「請求乘車」邏輯(Rider,Rider「 Lite」,m.uber.com)等)。

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

  5. 邊緣層。 將Uber服務安全地暴露給外界。該層也支持移動應用程序。

每層表明着愈來愈具體的功能分組,而且影響半徑愈來愈小(或者換句話說,更少的組件取決於該層中的功能)。

網關

在微服務架構中相信你們對「API網關」這個術語並不陌生。而在DOMA中咱們的定義的網關其實與你們所熟知的「API網關」的概念相差無幾,只是咱們傾向於將網關專門視爲 進入 基礎服務集合(稱爲 域) 的 單個入口點****。 網關的成功取決於API設計的成功。

因爲上游使用者僅在單一服務上運行,所以網關在遷移,服務發現以及總體系統複雜度方面提供了許多好處,上游服務僅需一個依賴項,而不是對域中可能存在的幾個下游服務的依賴。若是咱們從面向對象設計的角度考慮網關,那麼它們就是接口定義,它使咱們可以根據底層「實現」(在本例中爲底層微服務的集合)作咱們想作的任何事情。

擴展

擴展表示一種 擴展 域的機制。擴展的基本定義是,它提供了一種擴展基礎服務功能的機制,而無需更改該服務的實際實現,也不會影響其總體可靠性。在Uber,咱們提供了兩種不一樣的擴展模型: 邏輯擴展 和 數據擴展 。擴展的概念使咱們可以將架構擴展到可以獨立工做的多個團隊。

邏輯擴展

邏輯擴展提供了一種擴展服務的底層邏輯機制。對於邏輯擴展,咱們使用 提供程序 或 插件 模式的變體,其接口是以服務爲基礎定義的。這樣一來使得擴展團隊能夠在不修改底層平臺核心代碼的狀況下,以接口驅動的方式實現擴展邏輯。

例如,一個驅動上線。一般,咱們會進行各類檢查以確保容許驅動上線(安全檢查,合規性等)。這些都由一個單獨的團隊擁有。一種實現方法是讓每一個團隊在同一端點編寫邏輯,但這可能會增長複雜性。每次檢查都須要自定義且徹底不相關的邏輯。

對於邏輯擴展,「上線」端點將定義一個接口,他們但願每一個擴展都符合預約義的請求類型和響應。每一個團隊都將註冊一個負責執行此邏輯的擴展。在這種狀況下,他們可能簡單地獲取一些關於驅動程序的上下文況,而後返回布爾值,來判斷驅動程序是否能夠上線。「上線」端點將簡單地遍歷這些響應,並肯定它們其中是否有問題。

這就將核心代碼與每一個擴展解耦,並提供了擴展之間的隔離,它不知道其餘邏輯在執行什麼。圍繞這一點,就能很容易創建更多的功能,好比可觀察性或者是特徵標誌等。

數據擴展

數據擴展提供了一種將任意數據附加到接口的機制,來避免核心平臺數據模型中的臃腫。對於數據擴展,咱們利用Protobuf的 Any 功能,這樣團隊能夠將任意數據添加到請求中。服務一般會存儲這些數據或將其傳遞給邏輯擴展,這樣核心平臺就永遠不會負責反序列化(從而 "知道")這個任意上下文。Protobuf的任何實現都會有一些基礎設施開銷,以換取更強的類型化。爲了更簡單的實現,咱們能夠直接使用JSON字符串來表示任意數據。

自定義擴展

在邏輯和數據擴展以外,Uber的不少團隊都推出了本身適合本身領域的擴展模式。例如,與咱們的展現架構綁定的不少集成都使用了基於DAG的任務執行邏輯。

效益

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

DOMA在Uber還很年輕,咱們很高興能在將來分享更多的數據和咱們架構的深刻案例。不過,在簡化開發人員體驗和下降總體系統複雜度方面,早期的跡象是很是積極的。

產品與平臺

DOMA是Uber整個產品和平臺團隊達成共識的結果。平臺支持成本每每降低了一個數量級。產品團隊從護欄和加速開發中獲益。

例如,咱們擴展架構的一個早期平臺消費者經過採用擴展架構,減小了代碼審查、規劃和消費者學習曲線的時間,可以將一個新功能的優先級和集成時間從三天降低到三個小時。

下降複雜度

之前產品團隊要利用一個領域,須要調用許多下游服務,如今只須要調用一個服務。經過減小入駐新功能的接觸點數量,平臺可以將入駐時間縮短25-50%。此外,咱們可以將2200個微服務劃分爲70個域。其中大約有50%已經實施,其中大部分有一些將來採用的計劃。

將來的遷移

在Uber,咱們計算過微服務的半衰期是1.5年,也就是說每1.5年咱們就有50%的微服務流失。若是沒有網關,微服務架構很容易由於這種流失而陷入 "遷移地獄"。不斷變化的微服務須要不斷進行上游遷移。網關使團隊可以避免對底層領域服務的依賴性,這意味着這些服務能夠在不強制進行上游遷移的狀況下發生變化。

Uber在去年最大的兩次平臺重寫都發生在網關背後。這些平臺有數百個依賴於它們的服務,這些服務將不得不遷移現有的平臺。在這些狀況下,遷移的成本會很是高,使得徹底的平臺重變得寫不可行。

新的業務和產品線

事實證實,使用DOMA設計的平臺可擴展性更強,也更容易維護。Uber的大多數團隊之因此採用DOMA,是由於支持新業務線的成本過高。

一些建議

本節爲可能想採用DOMA的公司提供一些實用的建議。這裏的指導原則是,根據咱們的經驗,一個成熟的、通過深思熟慮的微服務架構源於在正確的時間向正確的方向悄悄推敲。現實狀況是,對於一我的的整個微服務架構來講,真正的 "重寫 "是永遠不可能的。

所以,咱們認爲微服務架構的演進更像是 "修剪樹籬",使其最終正確成長,而不是自上而下或一次性的架構(或從新架構)工做。這是一個動態和漸進的過程。

創業公司

驅動性的問題應該是 "咱們應該在何時採用微服務架構?"和 "它對咱們的組織有意義嗎?" 正如咱們在上面所看到的那樣,雖然微服務爲擁有大量工程師的組織提供了運營上的好處,但這也換來了複雜性的增長,會使功能的構建更加困難。

在小型公司中,運營效益可能沒法抵消架構複雜性的增長。此外,微服務架構一般須要專門的工程資源來支持,這對於早期階段的公司來講可能超出了預算,不然從優先級的角度來看是次優的。

考慮到這一點,在一段時間內徹底暫緩採用微服務也不是沒有道理的。若是一個組織真的選擇採用微服務,就應該考慮 "微服務做爲大型分佈式應用 "的類比,以及想要構建的微服務之間的關注點分離。另外,要認識到,第一批微服務極可能是最重要的,也是持續時間最長的,由於它們真正描述了業務的核心。

中型

一旦公司的規模達到中等,有了多個團隊,不一樣的功能和平臺之間的關注點明確分離變得朦朧,微服務架構就會變得更加明顯有用。

在這個階段,人們能夠開始考慮微服務之間的層次結構。依賴性管理可能會變得更加劇要,由於一些服務開始對業務運營變得更加明顯的關鍵,愈來愈多的團隊依賴這些服務。

早期對平臺化的投資可能會在將來的道路上獲得回報。若是可以建立徹底產品不可知的業務平臺,避免核心平臺服務中任意的產品邏輯,這裏就有可能避免技術債務。此時採用擴展來實現這一目標多是有意義的。

鑑於微服務的數量可能還至關少,將它們集中在一塊兒可能沒有意義。不過,這裏值得注意的是,在Uber的DOMA實現背景下,一個領域能夠包含一個服務,因此用 "面向領域 "的方式來思考可能仍是有用的。

大型

規模較大的工程組織可能有數百名工程師和微服務以及多個依賴關係。這時DOMA就達到了它的所有做用。極可能會有明顯的微服務集羣,這些集羣能夠很容易地歸爲域,在它們前面有一個網關。遺留服務每每開始須要重構或重寫,而後進行遷移,這意味着若是網關已經到位的話,很快就會開始在遷移的便利性方面提供價值。

明確的層次結構也將變得愈來愈重要,一些服務將做爲 "產品 "服務來運行,以實現特定的功能或功能分組,而其餘服務將愈來愈多地支持多個產品,並被認爲是 "平臺"。現階段關鍵是要保持任意產品邏輯與平臺的脫鉤,這樣才能避免給平臺團隊帶來沉重的運營負擔以及整個系統的不穩定。

最後的感想

隨着Uber愈來愈多的團隊來採用DOMA,咱們仍在積極地進化DOMA。DOMA的關鍵洞察力在於,微服務架構實際上只是一個大型的分佈式程序,你能夠將一樣的原則應用於它的演進,就像你應用於任何軟件同樣。DOMA只是一種在實踐中思考這些原則的方法。咱們但願其餘人以爲它有用,咱們也期待着反饋。


本文分享自微信公衆號 - JAVA高級架構(gaojijiagou)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索