微服務架構師的道、法、術(文字版)

2020 年 10 月,李鑫老師在 2020 CSDI SUMMIT 中國軟件研發管理行業技術峯會上做了《微服務架構師的道、法、術》的分享。以下是李兄事後整理的文字版本,擴散之。

我們首先介紹一些宏觀層面上的選型策略。

微服務的核心就在這個 “微” 字,衡量是否“微”,需要有一個參照物。

現實中,我們經常拿微服務和單體系統做比較。

以我們公司的基金銷售系統爲例,在早期的建設中,其實沒有什麼服務化和微服務的概念。當時的常用做法是有什麼類型的業務,就針對這種業務單獨開發一套獨立的銷售及清結算系統。由於業務量普遍不大,這些系統往往採用單體架構模式,不考慮橫向擴展性。這些功能常年累月堆積下來,就成了一個巨無霸系統,代碼量巨多、各種功能耦合在一起,由於要初始化很多東西系統啓動很慢,維護也很麻煩。一大堆人改一個系統,修改某個功能稍有不慎,就會對其它功能產生影響,導致後面沒人敢下手修改代碼。而且單體系統還有一個無法克服的問題,就是 “一死全死”,一個功能發生致命性問題,會導致整個系統崩潰。

如果把這麼一個巨無霸系統按功能拆分成一個個小系統並獨立部署的話,那麼單個系統的功能簡單了、體量變小了、維護的人少了,啓動變快了,開發也快了。一個系統出問題,其它系統仍能提供服務,影響也可控了,還能有足夠的彈性應對流量的波動。

當我們按這種模式將一個單體系統拆的足夠小,我們就可以將這些拆分出來的小應用稱爲 “微” 服務了,如圖 2。微服務的優勢就是 “小”、“快”、“彈性好”、開發快、上線快、靈活伸縮。

微服務適用場景

圖 3 企業業務及 IT 架構

既然微服務架構的特點是 “小” 而“快”,那麼判斷什麼樣的業務場景適合上微服務架構,自然也要從這兩個特點出發。

我們來看看企業的業務架構,從下往上可以看到,企業的業務包含了企業運營、生產製造、市場運營、客戶觸達四大領域。

其中企業運營是維持企業正常運作的基礎,支撐它的業務系統包括了企業的組織、流程、人事、辦公等,一般由內部 IT 負責。企業的組織架構和運作流程相對穩定,調整一般以 “年” 爲單位,我們很少看到一個企業三天兩頭調整組織和流程。相對應的系統也強調穩定,一般不追求快速迭代及變更。

再上一層是生產製造,支撐這層業務的系統包含了企業的產品生命週期管理、生產計劃排班管理、上下游供應鏈管理等。這些系統很多都是採購的商業系統,相互之間要花大力氣進行集成和打通,求穩不求快!隨着市場變化越來越快,精益製造和柔性製造理念的普及,訂單批量日漸趨於 1,個性化製造及效率的考量在生產領域越來越受重視,快的因素也在增長。

第三層市場運營,包括客戶、競品、市場、渠道等。這就需要較高的敏捷性了,要求企業能夠保持足夠的市場靈敏度,隨時感應市場變化,有效進行資源的調配和重組。因此支持這一層業務運作的系統要具有較高的靈活性,能根據市場需求隨時進行新功能的開發和部署,以支持一線銷售的需要。

最頂層的業務是與市場主體客戶的直接觸達,也就是終端消費。這是一個 “喜新厭舊”、追求熱點的時代,客戶羣體的口味時刻在變,一個熱度只能保持 3 天。我們做的 APP,營銷活動從規劃到上線基本以周爲單位來計算,這就要求這一層的支撐系統具有極高的敏捷形態,能夠快速開發、部署,並能應對流量的大幅波動。

總的來說,最底層的企業運營的迭代週期以年爲單位,追求穩定,是一種穩態 IT 模式。越往頂層迭代週期越短,追求的是一種敏捷性,可以稱之爲 “敏態 IT” 模式。可見企業 IT 架構中存在兩種 IT 形態,微服務 “小、快、彈性好”,剛好適合靠近市場的頂層業務領域。因此,市場運營和終端消費業務領域最適合採用微服務架構,變化越快,微服務架構的優勢越明顯。當企業面對激烈的市場競爭和多樣的終端渠道時,微服務架構是一個非常好的選擇。

微服務的問題

圖 4 微服務的問題

但是,也不要以爲採用了微服務就能走上研發的康莊大道,從此過上幸福生活。這個世界沒有銀彈,上帝給你開了一扇窗,就會關上一扇門。

我們確實能利用微服務架構解決單體系統的很多問題,但同時也帶來了一系列新的研發、運維及協同問題。

我大概總結了一下,它包括了服務治理、架構分層、統一監控、APM、穩定性建設、敏捷協同、精益度量、DevOps****、分佈式開發調測、契約測試等等。

微服務的度量

圖 5 微服務的度量

微服務的度量要解決的就是微服務的 “看” 的問題,這是對微服務進行管控管理的前提和基礎。只有全面瞭解微服務集羣的線上運行狀況及線下的開發效率、開發質量問題,才能 “有的放矢” 的提出針對整個微服務體系的問題。這些問題不僅針對架構,同時還針對開發、運維、研發組織及團隊協同等方面。根據問題進而制定改進措施和治理策略,並最終落地。

所以,只有先解決微服務的 “度量” 問題,才能解決微服務的 “管理” 問題

如何構建微服務的度量體系呢?本質上還是要從數據入手,準確的說是能夠衡量微服務狀態研發等領域狀態的指標數據。這些數據分爲線上數據和線下數據兩大類。

在線上,可以通過服務註冊中心獲取到服務的註冊信息及服務的管控指令信息,通過各個微服務主機節點上的主機日誌、應用及服務日誌、APM 監控的調用鏈日誌,可以獲取到相關的性能及異常指標信息。

線下的指標就更多了。通過需求管理系統,可以採集到 UserStory 及各類需求的基本信息;通過項目管理系統可以採集到開發人員、開發團隊、開發任務的基礎信息;通過測試相關的管理系統可以採集到測試用例及測試 bug 的相關定義信息及過程指標信息;通過源碼倉庫及軟件版本倉庫可以採集服務之間的調用關係,還可以通過代碼掃描獲取到服務之間的靜態調用關係,以及最終研發產出物的基本信息。

軟件研發是一個強調協同的羣體性行爲,產品、開發、測試、運維需要緊密合作。爲了達到更高效的配合,我們經常會採用一些協作模式,比如針對開發和測試之間的配合,採用持續集成(CI);針對產品、開發、測試的協作,採用敏捷協作模式;另外,我們可能還會使用一些 DevOps 的 Pipeline。不管採用何種協作模式,都可以從其相關的過程管理系統中抽取出相關的過程指標事件,比如一個任務什麼時候完成設計、什麼時候開始進入開發、什麼時候完成開發等等,這也是一大類很重要的治理度量指標。

將這些線上線下指標採集後統一彙總到數據倉庫,進行進一步的深度度量和分析。

這些原始指標以 ODS(操作型數據)的格式彙總到數據倉庫後,通過數據模型抽取出主數據(MDM),主數據包括了服務、需求、任務、人員、團隊等。在此基礎上,通過不同的數據主題構建起一個多層的 “數據集市”,這些數據主題包括了異常、性能、資源、調用關係、容量、系統、測試、開發、運維協同效率等。基於這個數據集市,就可以進行各類數據分析,包括性能分析、容量分析、健康度分析、團隊以及個人的質量報告、質量趨勢、動態調用鏈及靜態調用鏈的深度梳理以及各維度的彙總報表

根據這些分析報告,由治理委員會進行深度的分析並制定出各類的治理決策,通過人爲或自動化的機制發出各類管控指令。

所以,治理決策管控指令就是微服務度量及分析體系的最終產出物。我們通過治理決策和管控指令對微服務的線上及線下體系進行治理。

微服務治理架構

圖 6  服務治理的整體架構圖

圖 6 是服務治理的整體架構圖,它展示了度量、管控、管理三位一體的治理格局其核心觀點是:微服務治理既要進行線上的治理,也要進行線下的治理

前面介紹的線上、線下兩大維度的治理指標被統一彙總到數據倉庫中。這些度量指標中,有相當一部分線上的性能及異常指標會被直接轉化爲運維事件,一旦觸發預先設置的閾值,就會被進一步轉化成治理 “管控指令”,通過調度中心下發,驅動雲上的資源編排和調度能力,進行服務的彈性伸縮、擴容縮容操作,以應對流量的波動;或者直接驅動服務進行服務內部的限流、降級、容錯、路由調整等管控操作。

另一部分度量指標,特別是線下度量指標,例如架構、開發、測試、運維、過程協作效率等會通過**治理委員會(泛指,治理成員的集合)**進行人爲的深入分析,制定出治理決策。這些治理決策包括技術決策、研發團隊調整、過程優化、技術選型決策、線上資源調配、線上系統管控等等。治理決策會通過相關的管理措施進行落地。

這樣,我們通過服務的度量、管控、管理三大舉措,構建起一個三位一體、圍繞服務治理的閉環體系。在這套體系中,針對服務的度量是進行服務管控和管理的前提和基礎,只有看得到,才能管得到,必須先解決 “看” 的問題,才能解決 “管” 的問題。

微服務架構選型

圖 7 微服務框架的架構模式

圖 7 是目前微服務框架的兩大架構流派,一種是 “網關代理連接模式”,一種是 “客戶端直連模式”。這兩種方式的服務提供者都會在服務啓動時到服務的註冊中心去註冊。但在網關代理模式下,服務調用者只通過網關調用服務,不關心服務提供方的具體節點信息;直連架構的調用者會從服務的註冊中心去拉取相應服務的節點列表。

這兩種模式最大的區別是路由、容錯和負載均衡策略的實施主體不同。代理網關模式進行服務調用、服務治理的主體是服務代理網關,客戶端不需要關注這些治理動作,甚至不需要知道服務有多少個節點,所以客戶端可以做得很輕巧,除了連接協議之外,對客戶端的開發語言沒有什麼約束,嵌入的 SDK 很輕巧甚至不需要 SDK。直連模式由於沒有代理網關,所以服務的路由、容錯、負載均衡策略的實施有很大的比重都需要在客戶端進行,客戶端需要拉取對應服務全量的服務節點信息,並時刻關注節點的上下線情況,所以嵌入的 SDK 很重,與服務框架會形成強耦合的關係。

但是,由於代理網關模式在網絡上多了一跳,爲了兼容多樣的客戶端調用,網絡在服務調用方和代理網關之間通常採用 HTTP 這種通用的短連接協議。而直連模式由於是客戶端直接調用的服務提供方,可以採用更高效的 RPC 長連接模式,比如基於 netty 的 NIO 長連接協議,所以在處理效率上 P2P 直連模式往往要高於代理網關模式。對於很多大型互聯網公司,由於線上調用量大,RPC 方式可以有效提高處理效率,因此這些公司通常會採用 P2P 直連模式,這樣可以有效的節省服務器資源。比如國內公司用的很普遍的 dubbo 或者螞蟻金服提供的 sofa 等,都是採用 RPC 直連模式。SpringCloud 同時提供了兩種框架,既可以使用 Ribbon 提供的 P2P 直連模式,也可以基於 zuul,現在叫 SpringCloud Gateway,來實現代理模式。由於 SpringCloud 的生態比較全,而且又有 Privotal 這樣的公司在持續維護,目前市場佔有率越來越高。

但從服務治理的角度看,代理網關模式是要優於直連模式的。由於所有的調用都要集中通過代理網關,所以對整個微服務集羣的監控指標採集,路由、負載、容錯等管控措施都可以在網關集中實施,不分散,可控性也比較強。而直連模式的治理主體包含了各個服務調用方,指標採集的難點,管控的難度都呈指數上升。集羣規模越大,這種難點差異越明顯。

所以,究竟選擇那種微服務框架,最終還是要基於業務規模、研發資源投入、人員水平來綜合考慮。另外,我個人建議,如果要在微服務方面做到可持續發展的話,前期就需要考慮服務治理的問題。

根據康威定義,整個組織的協同溝通模式及行事風格,要和你所採用的架構體系相匹配,否則一定會出問題。

因此,一旦確定引入微服務架構,接下來就是很現實的適配問題,包括微服務架構與業務及研發體系的貼合度問題。如何利用微服務架構順暢的落地業務需求,如何讓研發團隊在這種架構模式下高效的設計、開發、運維,並協同配合。

接下來,我們就介紹一些採用微服務架構的方法論。

微服務的規劃

圖 8 微服務規劃的三個問題

微服務的規劃是一個很大的話題,但總結一下,無非是回答 3 個問題。

第一:要不要上微服務?

這個問題我們在前面討論 “企業的哪些業務領域適合上微服務” 時已經有了結論,越貼近市場的業務,迭代變更速度越快的業務越適合上微服務。前提是需要有一整套支撐微服務架構的能力體系,包括服務治理,強大的運維能力等等。對於一個創業團隊,尤其是精益創業團隊,很難在短期內構建這套能力,因此創業團隊不要太刻意追求微服務。早期功能不多的時候,單體架構對技術能力要求低,維護成本也低,是很合適的選擇,功能複雜了再考慮往微服務架構遷移也來得及。

研發團隊經常犯的另一個問題是,喜歡基於技術潮流來選擇技術,而不是基於業務來選擇技術。我們經常會說 “別人都在上微服務了,我們也要上”!這就本末倒置了!技術是爲業務服務的,基於業務的需求來考慮適合的技術,纔是技術選擇的初心。

我想強調的是,不要糾結於具體的技術選擇,畢竟唯一不變的是變化。一種技術不能包打天下,單體架構不行,微服務架構也不行。業務不斷髮展,對應的系統也要不斷的進化,不斷根據業務的需求及環境進行升級和重構。任何一種架構也只是適用於當下,在未來的某個時刻,當業務發展到新的階段,它也必須隨之改變。因此,我們最應該做的是保持進化的心態,構建起一套持之以恆的架構優化體系,通過持續的架構優化,不斷對每個階段的架構體系進行監控和調整,讓它能夠適配業務發展的要求,這纔是架構的可持續之道。

第二:如何定義微服務

我在很多場合都被問到一個問題:怎麼把當前的業務或者當前的一個單體系統拆成微服務?我的回答是:按業務領域拆!業務域本身也是分層的,比如對基金交易業務來說,可以分爲用戶域、交易域、資產域等等。用戶域又可細分爲客戶域和賬戶域。再細分,還有認證域和授權域。當你一層一層的細分下來,直到業務上已經無法再細拆的時候,就是微服務了。每個服務還可以包含若干個 API,比如登陸服務,基於賬號密碼登陸是一個 API,基於微信或支付寶的第三方認證登陸是一個 API,靜默登陸可以是另一個 API。這樣,一個微服務的定義就明確了。從一個單體系統逐步的拆分成微服務架構也可以按這種模式來,一步步、一點點的把功能逐步遷移過去。Martin Fowler 將這種策略稱爲絞殺,還是比較形象的。這也符合系統是進化而來的規律,舊的單體系統慢慢消亡,微服務應用在逐步成長。

這兩年領域驅動設計(DDD)很火,關鍵是大家發現它的一些設計思想包括子域、限界上下文、映射、聚合等等與微服務架構天然能對應上。但它本質上還是面向對象的設計,只是在對象之上增加了域的隔離和交互等能力,它的有些概念比較拗口,對於是否需要深入研究領域驅動設計大家不用太糾結。我個人的經驗是,只要堅持從業務域劃分面向對象設計,你的微服務設計一般不會太差。學習一些 DDD 的好處是在做複雜業務的微服務設計時,條理性更好,同時通過 DDD 這套領域設計語言,實現需求結構化,讓業務人員和開發人員能夠更順暢地對話。

另外,不管多麼精心設計出來的微服務架構也不能包打天下。必須承認我們的認知是有限的,只能基於當前的業務狀態和對未來演變的有限預測來制定相對合適的拆分方案。任何方案不管多完美,都只能保證在當下是相對合適的,要時刻做好在未來的某個時刻會變得不合時宜需要再次調整的準備。所以,變化是永恆的,我們要時刻關注微服務的拆分粒度是否合適,如果不能滿足需求就要繼續拆分它。

在實際操作中,我們每一期的迭代開發,除了業務需求還有架構優化需求,就是用來應對這些架構技術層面自我進化重構的任務。

第三:多小纔是 “小”

我們在討論按業務領域拆分並定義微服務時曾說過,當你感覺拆無可拆的時候,它就是微服務了。那麼,是否每個微服務都要獨立部署呢?其實不然,邏輯劃分實際部署是兩回事。理論上,每個微服務都可以獨立部署,但是以資源無限爲前提的。實際應用中,我們往往將微服務合設進行部署。一個一級業務域或者二級業務域下的微服務會統一成一個應用部署,這樣可以節省硬件資源和降低維護成本。從這個角度看,實際的微服務應用粒度可以很大,不用太糾結 “多小纔是小” 這個問題。開發和運維覺得合適就可以了,反正重構是常態,不合適繼續拆,或者合併,就行了。

圖 9 微服務及調用關係示例

我們基於前面介紹的規劃方法進行微服務拆分。 結果拆出了一大堆微服務,密密麻麻的調用關係如圖 9 所示。

這張圖給我們第一直觀感受是什麼?

多!

,就意味着複雜。圖 9 中服務之間的關係已經超出了人眼可以辨識的範疇。所以,需要對這些微服務進行組織和管理,讓它整體更清晰。

服務分層是降低服務化架構體系整體複雜度的一種有效方式。

微服務架構分層

圖 10 兩層服務分層示意圖

爲了便於理解,這裏以我們的基金交易平臺的服務化作爲例子。

在系統進行服務化改造的早期,我們主要是將各系統中通用的能力,如用戶賬戶、交易、支付、資產、結算、權益、投顧等功能抽取出來,改造成服務後以服務集羣的形式獨立部署,統一對上層應用提供通用服務。這就是我們內部俗稱的 8 大服務中心,它們共同構成了後臺的通用服務層。原來的業務系統拆分後變得更輕了,更多的充當了一個交易大廳的角色,我們稱它爲前臺服務層。

我們的網關層共提供了兩套網關,一套是 SLB,另外一套是移動網關。用戶的交易請求通過網關層被統一接入進來,先在前臺服務層進行協議解析、安全校驗和簡單的業務邏輯處理後,通過調用後臺通用服務層的服務進行基金的相關交易。

由於早期前端的業務渠道不多,業務也比較單一,這樣的兩層服務化的拆分已經夠用了,前臺服務層的粒度足以支撐正常的業務迭代速度

圖 11 三層服務分層示意圖

隨着業務進入快速發展階段,大量的終端渠道接入,業務迭代的速度也明顯加快。舉個例子,我們有一個叫 “目標贏” 的定投產品,在很多終端渠道上進行銷售,每個渠道對這個產品都或多或少有一些個性化的訴求,有在客戶校驗上增加特殊要求的,有要求在產品銷售上疊加一些額外的運營規則的。綜合來看,這些終端渠道對 “目標贏” 的功能訴求中,有 80% 是一致的,有 20% 的個性化訴求。如果按早期的兩層服務劃分的方式,我們需要爲每個服務都開發一個重複度很高的功能,這明顯不划算,太重了。怎麼辦呢?繼續拆!將 “目標贏” 裏與渠道無關的 80% 的通用功能拆出來繼續下沉,形成了一個新的服務分層,我們稱它爲業務服務層。這樣還不夠,繼續將前臺服務層中剩下的 20% 個性化能力中與業務無關的部分,例如協議適配、拆包、安全校驗等功能拆出來,與網關層合併形成了安全網關這個新的擴展。這樣,前臺服務層只剩下了 10% 左右的業務功能,裏面大部分的邏輯是對業務服務層及通用服務層的能力進行組裝聚合。前臺服務層已經被拆的很輕了,我們可以改稱它爲聚合服務層。通過這種繼續拆分的方式,將早期的兩層服務化分層架構,演進成包含聚合服務層、業務服務層和通用服務層的三層服務化分層架構。

17 年之後,“業務中臺”這個概念大火,我們發現三層服務化分層架構中的 “業務服務層” 與業務中臺定義高度匹配,把它定位爲基金銷售業務的業務中臺也是完全 OK 的。因此,聚合服務層、業務服務層和通用服務層這樣的三層分層定義換個說法就是業務前臺、業務中臺和通用後臺。這樣的分層架構並非我們刻意爲之,而是基於業務的驅動,在服務化的架構下自然形成的。

圖 12 架構演變

將我們線上系統的架構變遷的整個歷史拉通來看,可以看到明顯經歷了 3 個階段。

早期是典型的單體 + 豎井架構,複用性及可擴展性都差。

在服務化的初期,將系統進行了兩層服務化的拆分:前臺業務服務層 + 後臺通用服務層。當業務發展比較平穩,前端業務渠道不多的時候,兩層服務化分層足以支撐業務的正常迭代。

隨着業務的快速發展及多端適配需求增多,兩層架構下的前臺業務服務層還是太重,無法支撐業務的快速變更。這時需要繼續拆分,將業務域的通用服務繼續下沉出新的業務中臺層,這就形成了現在的三層服務化分層架構。

在目前的三層服務分層架構下,通用服務被不斷下沉,因此越底層的服務抽象度越高,越通用也更靜態,不會經常改變;越靠近前端的服務越貼近業務也越不穩定,會隨着業務的快速變化而不斷改變,客觀上必須保持更輕的體態。

業務中臺的存在解決了兩層服務化架構下前臺業務服務和後臺通用服務之間的適配問題。可以將前臺服務的粒度拆分的更細,讓前臺服務不用通過大量的代碼來處理業務邏輯,只需要少量的粘合劑代碼來對中臺專屬業務領域的通用服務和後臺跨業務領域的通用服務進行快速組裝和聚合,從根本上降低了前臺服務開發的工作量和成本。

“快” 是很重要的一種能力!

業務變化越快,多端適配的需求越多,中臺建設的收益越大。如果業務比較平穩,沒那麼多的業務渠道,兩層服務化架構就能工作得很好,服務分層和拆分的需求就不那麼強烈,進行中臺建設的意義也就不大。因此企業應該根據自身的業務特徵來判斷是否需要建設業務中臺,順其自然。

架構治理

圖 13 線上服務集羣調用關係

在傳統企業級開發中,一般遵循先架構設計再開發的協同聯動原則。但在服務化架構下,由於原來的單體系統被拆的很散,傳統架構師的職責更多的被分散到一線研發團隊,由開發人員來承擔,架構師的職責貌似被減弱了。而且由於服務的粒度比一般應用小,架構的工作也好像沒有以前那麼重要了。

恰恰相反,在離散化體系中更要加強對架構的管控,以防止整體架構劣化。與傳統的架構先行的模式不同,服務化下的相當一部分架構工作被 “後置” 了,成了一種事後行爲,也就是說架構度量和優化的工作會大幅度上升。這裏我們主要討論如何通過服務之間的調用關係來針對服務化的整體架構進行度量和治理。

圖 13 是線上服務集羣服務間的調用關係總圖。這個圖可以通過動態調用鏈的彙總來獲取,目前大部分公司都是這麼幹的。但使用動態調用鏈有個很大的缺陷,調用鏈只有在運行時才能生效,而且必須有埋點並實際發生的調用才能被監控和採集數據。對於一個複雜的平臺或大系統而言,通常有大量的冗餘分支異常處理邏輯,這些分支及邏輯,往往需要在特定場景下才會被觸發,甚至有可能永遠都不會被觸發。所以通過動態調用鏈抓取的這個服務之間的調用關係是不完整的。我們除了使用動態調用鏈,還開發了靜態代碼掃描技術,通過代碼之間的調用關係來生成調用關係圖,這種方式可以有效彌補調用鏈的不足。基於微服務間的整體調用關係,我們可以採用圖論的相關算法對微服務的調用質量進行深入的分析。

服務是分層的,好的服務調用關係也是分層的,層層往下推進,最終形成一個有向無環圖(DAG)。因此,可以對調用關係圖進行閉環檢測。如果檢測到如圖 G 點到 B 點的迴環調用,說明調用關係是有問題,需要進行優化。這種迴環調用現在也許無感,但難保未來哪天就會由於一條旁路邏輯導致死循環。

可以對整個調用網絡進行遍歷計算,找出所有調用深度最深的調用鏈,如圖 13 紅色標註出來的調用鏈。對跨網絡的調用訪問,涉及到的網絡節點越多,穩定性越差。可以將所有調用鏈路最深的鏈路找出來,按調用深度進行 topN 排序,重點分析排在頭部的調用鏈的必要性和合理性,看是否能對調用深度進行縮減和優化。

還可以找出整個網絡中被調用最多的服務節點,比如圖 13 的 F 節點。從調用關係上來說,它是被依賴最多的節點,自然是最重要的節點,作爲樞紐節點,在運維等級上需要重點保障。實際應用中,我們還會加上調用量這個權重來綜合判定服務節點的重要性。

隨着架構的不斷演進,可能有些服務節點再不會有調用關係了,比如圖 13 中綠色的 L 節點。這些節點再不會去調用別的服務節點,別的服務節點也不會來調用它。這類 “孤零零” 的節點被找出來,可以考慮對它進行下線處理,以釋放資源。

如果有了服務之間的完整調用關係,一旦某個服務需要修改,我們就可以循着調用鏈路逆流而上,找到它可能影響到的前臺業務。這樣,不用等到服務上線,在修改之前就能評估到它的變更影響,從而降低服務變更的風險。

以上所有的度量和治理都是在調用關係圖的基礎上進行的,所用的算法也是圖計算(圖論)中的常用算法,包括 BFS、DFS、PageRank 等等。大家如果嫌麻煩,可以找個圖數據庫(比如 neo4j),這些算法已經集成在它的基本查詢能力中。

DevOps 體系

圖 14 DevOps 體系示例

我們在前面討論了企業引入微服務架構最主要的目的就是要 “快”,要實現價值快速交付

微服務架構通過 “” 的方式解決了單體系統的解耦問題,但也導致運維工作量直線上升。拆分後的微服務的編譯、打包、部署及背後的環境維護工作是原來單體系統的幾倍到幾十倍。如果沒有自動化工具和平臺的支持,研發和運維人員都將寸步難行。 採用微服務架構之後,由於工程數量多了,上線頻度也快了,產品、開發、測試、運維幾個團隊的協同配合難度也將升高。

要解決這些問題,需要我們去構建完善的 DevOps 體系。

DevOps 的理論體系這裏就不多說了,下面重點介紹我們是怎麼玩的。

1、業務需求分解爲 UserStory 後,被逐項整理爲 Jira 的工作項,進入 backlog 中。

2、迭代開始後,在 Jira 中創建迭代對應的 Sprint,並從 backlog 中撈一批工作項,指定開發人員,進行需求下發,同時配置管理員在 Git 中創建新的開發版本分支。

3、開發人員利用開發機進行編碼開發。

4、提交的代碼會觸發流水線的構建服務,調用 Maven 進行編譯,生成構件。

5、構件生成後會通過自動化的單元測試和集成測試對它進行校驗,只有通過測試驗證的構件纔會被提交到構件倉庫;如果沒有通過測試驗證,則終止發佈活動。

6、手動或者自動進行部署。部署過程直接從雲上申請資源,我們用的是螞蟻金融雲,這個過程會從構件倉庫下載指定構件。環境部署過程中,會通過預定義的各種驗證機制進行環境可用性的驗證。

7、一旦部署通過就可以測試了,這個階段包含手工測試和自動化測試。自動化測試環節會調用預先定義的大量自動化腳本進行 API 接口測試,或者調用 Selenium、Appium 這類自動化測試工具進行功能驗證、性能測試等等。測試通過,測試團隊會發出測試準出指令。如果測試不通過,就會在 jira 裏生成指定 bug 工作項,讓研發人員進行缺陷修復,直到所有缺陷都被修復,測試準出。

如圖 14,在整個流水線流轉過程中,我們會用到 3 個看板:

u 管理看板:監控需求流轉狀況及效率;

u 精益過程看板:監控、分析整個協同流水線的運行效能;

u 運維看板:主要涉及到環境的運營監控。

以上這些看板都需要數據的支持。所以,在流水線流轉過程中,一方面研發人員要基於自身任務的完成狀況及時更新 Jira 中工作項的狀態;另一方面,也可以基於工具的執行結果自動採集或推送需求狀態。這樣,Jira 中的需求看板就能實時展示當前任務的完成情況。採集流水線各個環節的執行過程指標,包括任務完成狀態、開始時間、任務耗時等,彙總計算後再通過精益看板對研發過程進行度量和分析。至於運營監控,則是採用運維監控看板展示各個環境中的相關日誌、告警信息等。

圖 15 服務化架構下的研發痛點

在服務化的過程中,研發遇到的第一個困難,一定是調試。原來單體應用中的服務被拆分到不同團隊,部署在不同的服務器上,而本地只有一個服務接口。這時候要做調試,要麼做 P2P 直連,要麼做 MOCK。採用傳統的 MOCk 手段,要寫一堆的 MOCK 語句,比如用 mockito,就要寫一堆的 when…..thenReturn…. 的語句,耦合度非常的高。

我們是利用分佈式服務框架提供的過濾器機制,開發了一個 Mock 過濾器,通過 Mock 數據文件來詳細定義要被 mock 的服務的名稱、入參及出參。當請求過來的時候,將服務名及入參和 mock 數據中的定義進行比對,結果吻合就將 mock 數據文件中的出參反序列化後作爲服務的調用結果直接返回,同時遠程調用的所有後續操作被終止。這樣,就通過 mock 數據模擬了一個真實的遠程服務。

通過這種方式來構建服務的 mock 能力,就不需要寫一堆的 mock 代碼了,而且整個過程對業務邏輯來說完全無感知,把 mock 能力完全下沉到底層的服務框架。

另外,爲了有效降低製作 mock 文件的成本,我們開發了一些輔助工具,可以基於服務接口定義直接生成 mock 文件的框架。我們還基於服務框架過濾器機制開發了 “在線數據抓取過濾器”,它可以將指定的服務請求的入參和返回結果都抓取下來,直接寫成 mock 數據文件。通過抓取方式獲得的 mock 數據文件,往往有更好的數據質量,畢竟反映的是更加真實的業務場景。不過,這有合規性的問題,一定要做好數據的脫敏處理。我們目前只在測試環境中進行數據抓取操作。

我們的綜合調測能力是綜合 P2P 直連和 Mock 兩種方式來共同構建的。綜合調測能力的構建可以有效改善服務化架構下團隊的開發效率,而且團隊規模越大,效果越明顯。

所謂術,就是一些微服務的監控、度量和管控的具體方法。

度量之術

圖 16 度量基礎

我們做服務度量的具體方法,就是由點到線,再到面,最後構成一個監控及度量的立方體。

一個最簡單的跨網絡的請求,如圖 16 的左邊。如果我們把每個請求對服務的調用耗時和它的調用狀態都記錄下來,所謂狀態就是成功或者失敗,如果失敗還要額外記錄失敗信息和失敗碼。同時也可以記錄服務對外部服務,包括數據庫、緩存、消息隊列的調用耗時和狀態;記錄操作的動作,比如對數據庫的查詢插入刪除等,以及所操作的是哪個表等等信息。 所有這些信息裏面,最核心的就是兩個指標,調用延時調用狀態

有了單次請求的調用指標,進一步彙總一分鐘之內所有請求的這些指標,疊加後就得到了圖 16 右邊的示意圖。可以看到在這一分鐘之內,各服務的調用系的調用情況,包括調用次數、成功和失敗次數、總耗時等詳細信息。這就是單個度量時間段的基礎彙總指標。同樣的,對其它服務和資源的彙總統計也按這個模式來。當然,對資源的度量會更細一些,我們會計算 update 操作發生了多少次,select 操作進行了多少次,維度也會更多一些。

這樣,單次請求的指標就構成一個,一分鐘單個節點的彙總指標就構成,再把一個服務下所有服務節點的信息進行彙總就構成了描述一個服務的完整的。再彙總所有服務及資源的調用狀況就構成整個微服務集羣度量的指標立方體了。

要注意的是,一分鐘是基礎彙總維度,在這個基礎上還可以做小時、天、周、月的彙總度量。 這就是我們針對服務度量的最基礎的方法。

圖 17 性能度量

線上服務最核心的兩個度量維度,一個是性能,另一個是異常。性能度量是微服務度量工作的基礎,也是最重要的工作。

性能度量最核心的幾個彙總計算指標是性能最差服務度量,總資源佔用最多服務度量以及這兩個指標的一些演化指標,包括歷史同期的橫比指標以及性能分佈等等。在性能報表上,我們還會疊加調用量等指標,便於多維度的分析。如果一些服務調用耗時高,調用量又大,那麼出現阻塞的風險就會很高,大概率存在一些隱患,需要具體進行分析。

另外,對調用耗時還可以看它的分佈,查看一些長尾效應,考察有沒有性能毛刺。如果有,尤其是有周期性出現的 “毛刺”,它本質上和系統的“脆弱性” 有關。在高併發 / 大負載等極限情況下,這種 “脆弱性” 將被放大,可能給系統造成嚴重影響。通過分析調用耗時這類性能分佈圖,很容易發現這類異常指標。

性能指標有時也要結合系統指標進行綜合分析,低負載狀態和高負載狀態下,就算性能指標一樣,系統的健康程度也是有巨大區別的。爲了獲得更客觀的系統性能指標,有時還要綜合一些系統及服務容器的指標進行綜合分析。

圖 18 異常度量

服務度量的另外一個維度是異常,異常的準確定位有時很複雜。因爲異常是會傳導的,它會從底層往頂層,從服務的被調用方向服務的調用方傳導。 所以,對異常的度量往往要基於關係來進行分析。我們日常收集的日誌是散的,這些線上日誌往往描述的是單個點的狀態。所以,我們儘量要通過其它的一些手段來對這些離散化的日誌進行關聯。

舉個我們進行線上故障定位的例子。我們有個線上的接口監控報警,如圖 18-1。服務集羣中的一個服務(A 服務)週期性的出現操作異常率偏高的現象,監控數據顯示大量的異常都是由於請求耗時延長,超出了 API 預定的最大延時閾值而被服務框架主動終止請求導致的。 這時,我們將正常時段的監控數據和異常時段的監控數據分別疊加在這個 A 服務的靜態調用鏈路圖上,如圖 18-2。可以看到,在這個異常時段 A 服務對大後臺的一個服務(B 服務)的接口的調用比例非常高,而且調用延時也從正常的幾十毫秒飆升到 1000 多毫秒。我們高度懷疑可能是其它業務對 B 服務的調用擠佔了資源,從而造成調用堵塞。基於這個假設,以 B 服務爲起點,調看它的 “被調用關係圖”, 如圖 18-3。果然,除了 A 服務外,還有一個定時批處理任務也調用了 B 服務,而且在異常時段對 B 服務的調用量非常大。查看處理邏輯,發現這個批處理任務是用來遍歷所有用戶並進行用戶積分變更操作的,這個過程中,它會調用服務 B。由於用戶數量特別多,它在並行分片處理時沒有做限流,短時間內會發起大量的針對服務 B 的調用從而導致資源被擠佔。服務 B 的調用延時雖然增長,卻沒有超出預定的最大延時,也就是說,服務 B 不會報錯。但性能瓶頸傳導到服務 A 後,直接導致了服務 A 的調用延時超長,超過微服務框架預設的最大閾值,最終導致請求被終止,引發請求失敗故障。故障查明後,對這個批處理進行限流操作,同時對它的調用做了緩存處理,問題解決。

這就是典型的由一個問題導致另外一個問題的故障傳導。傳統監控中,監控數據在呈現模式上是 “散” 的,很難發現相互間直接的聯繫。我們通過引入靜態調用鏈路圖,通過調用鏈的關係來關聯監控數據,讓監控數據從 “無序” 變成了“有序”,從而爲我們提供了快速定位問題根源的途徑。

服務治理以及度量的過程本質上就是尋找數據關係的過程。所以在實際治理工作中,要通過各種手段來獲取數據之間的關係,包括時間、空間、業務等各個維度。靜態調用鏈路僅僅是其中的一種手段而已。只有獲取了數據的關係,才能夠順藤摸瓜,找到問題癥結及治理策略。

19 調用鏈跟蹤

調用鏈跟蹤算是分佈式環境下故障定界定位最有效的工具了,是更高級的故障定界定位手段。相關產品也很多,開源的有 Zipkin,SkyWalking、PinPoint、CAT,商用的有聽雲、AppDynamic 或 NewRelic 等等。

調用鏈本質上也是基於日誌,只不過它比常規的日誌更重視日誌之間的關係。在一個請求剛發起時,調用鏈會賦予它一個跟蹤號(traceID),這個跟蹤號會隨着請求穿越不同的網絡節點,隨着日誌落盤。日誌被收集後,可以根據 traceID 來對日誌做聚合,找到所有的關聯日誌,按順序排序,這樣就能構建出這個請求跨網絡的調用鏈。

要更好的利用動態調用鏈需要和監控大盤相結合。我們的經驗是,動態調用鏈跟蹤體系構建得比較早,在監控大盤上有很多的點可以進入調用鏈。例如,我們有一個單位時間段內異常最多服務的 TopN 排序列表,點擊列表上的任何一個服務,可以打開這個服務在這個時間段內所有異常的列表,點擊列表上的每一個異常,就會打開這個異常所屬調用鏈,進行故障分析。還可以利用監控大盤,監控大盤上有很多 “毛刺”,這些都是系統的異常點。點擊任何一個“毛刺”,會將“毛刺” 所在時間段內的請求以 “散點” 的形式列出,“散點”的顏色代表了不同的狀態,有的成功,有的失敗。點擊任何一個“散點”,就可以進入這個請求對應的調用鏈。針對核心服務的異常也有專門的一個監控表格,會列出最近發生的核心鏈路服務上的異常,點擊這上面的任何一個異常,同樣可以進入對應的調用鏈。

以上就是基於動態調用鏈進行線上故障定界定位的常用模式。

管控之術

以上介紹了一些服務度量的方法,解決了看的問題,接下來開始管。對微服務進行管控又有哪些手段呢?

圖 19 管控之術——限流

首先介紹服務限流。

服務限流是微服務集羣自我保護的一種常用機制,我們對線上調用比較頻繁和資源佔用較大的服務都加上了相應的限流舉措,並構建了單機限流及集羣限流兩套限流措施。

單機限流有多種限流算法可供選擇,最主要的是兩種,漏桶算法令牌桶算法。它們之間有什麼區別呢?打個比方,疫情期間很多餐廳都限制客流,一種舉措是出來一個顧客,才放進去一個顧客,保證餐廳中的顧客總數是固定的,人人都有座位,這就是漏桶算法——必須有 “漏” 出去的,纔能有進來的。另外一種舉措是不管有沒有顧客出去,門口招待固定每隔 5 分鐘就放一波客人進來,這和春運火車站的波段式限流非常類似,可以保證客流比較均勻。但這種策略的風險是,如果餐廳中的顧客離開得不夠及時,餐廳中的顧客總數可能會升高,導致一部分顧客沒有座位,這就是令牌桶算法。因此,如果要對線上併發總數進行嚴格限定化,漏桶算法更合適。這是單機限流機制。

集羣限流的情況要更復雜一些。首先在各個微服務節點上都要有一個計數器,對單位時間片內的調用進行計數,計數值會定期彙總到日誌中心,由統計分析器進行統一彙總,算出這個時間片的總調用量。集羣限流分析器拿到總調用量後與預先定義的限流閾值進行比對,計算出限流比例。這個限流比例通過服務註冊中心下發到各個服務節點上,服務節點基於限流比例各自算出當前節點對應的最終限流閾值,最後利用單機限流進行流控。

可以看到,這是一套環環相扣、各環節緊密協作配合的技術體系。單純拎出一個點來看,實現技術都不麻煩,但要構建一套貫穿整個技術棧的技術體系,則需要有一套統一的技術標準。各個環節都要遵循這套標準,對不符合標準的應用要推動其進行改造,保證標準落地,才能構建起這套限流技術體系。因此構建服務限流能力的難點有兩個,標準化體系化

體系化的構建是最難的,但也是最有威力的,大公司往往就是依靠體系的力量去構建起護城河,碾壓沒有體系能力的小公司。

限流一大原則是限流動作儘量前置,畢竟被限制的流量註定要被 “拋棄”,越早處理越好,免得無謂的消耗資源。

圖 20 管控之術——降級

服務降級和服務限流類似,也是微服務集羣自我保護的機制。一般在線上動用服務降級手段的時候,都是比較危急的時候,生死存亡了,這時候留給你思考和反應的時間不多。所以在使用服務降級之前一定要做好預案,要提前梳理出核心業務鏈路和非核心業務鏈路,然後通過降級開關一鍵把部分或所有非核心鏈路降級,這樣才能救命。

服務降級也有很多手段可以使用,包括:

u 容錯降級

u 靜態返回值降級

u Mock 降級

u 備用服務降級

我們常說的熔斷,本質上也是容錯降級策略的一種,只不過相對一般容錯降級,它提供了更爲豐富的容錯託底策略,支持半開降級全開降級模式。

構建服務降級能力也和限流機制類似,同樣需要堅持標準化和體系化。

圖 21 管控之術——容錯

集羣****容錯是微服務集羣高可用的保障,它有很多策略可供選擇,包括:

u 快速失敗(failfast)

u 失敗轉移(Failover)

u 失敗重試(Failback)

u 聚合調用(Forking)

u 廣播調用(Broadcast)

很多容錯策略都是靠重試來實現的,但不管你使用哪種集羣容錯策略,都要注意重試的次數,尤其是線上負載已經很高的時候。這時候如果重試次數過多,一方面會推高服務被調用方的負載及併發,另外一方面會導致服務調用方的調用延時增長,雙重因素疊加之下,最終極可能引起 “服務雪崩”,導致集羣被 “擊穿”。

所以,在使用集羣容錯時,一定要設置最大重試次數。另外,有可能的話,儘量做 “重試減速”,也就是讓每次重試的間隔時間越來越長(可以考慮採用指數),這樣可以將重試帶來的壓力盡量分散到一個足夠長的時間段,避免阻塞在一起從而人爲造成 DDOS 攻擊。

總結

微服務架構的道、法、術囊括了從體系到方法論,再到具體算法的三個層面。其中 “**道” 是體系,**主要討論微服務應用場景,微服務帶來什麼問題,要解決這些問題需要構建什麼體系,以及我們如何根據業務去選擇微服務的模式。 “法” 是方法論,包括微服務的規劃,還有架構分層,如何更高效的協同。“術” 是具體的方法和技術,包括微服務的度量,度量裏面也包括性能,還有微服務的管控,包括限流、降級、容錯等等。

這就是我今天分享的內容。謝謝各位!

最後——

我把十幾年的經驗總結起來,出了本書,叫做**《微服務治理:體系、架構及實踐》**,大家如果感興趣可以關注一下。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/UtOaSkrDqBmce8eDon0HnQ