BFF
前面處理了服務間資料依賴的場景。
除了這種頻繁需要其他服務的資料的場景,其實還會碰到服務間依賴太雜亂的問題。
本篇讨論的就是如何緩解服務依賴複雜度的問題。
先把整個業務場景描述一下。
業務場景:如何處理好微服務之間千絲萬縷的關系
本節所講的系統包含商品、訂單、加盟商、門店(營運)、工單(門店)這幾個服務,其他服務就不細說了。
除了一個App面向客戶以外,還有一個App是給公司的員工和加盟商的員工使用的。裡面有各種角色的使用者,比如總部商品管理、總部門店管理、加盟商員工、門店人員等。當然,每個部門裡面還會細分角色。
背景服務架構如圖15-1所示。
• 圖15-1 背景服務架構
其中,網關層負責如下工作。
1)路由:所有的請求都會通過網關層,網關層再根據URI把請求指向對應的背景服務,如果同一個服務有多個伺服器節點,網關層還會做一些負載均衡的工作。
2)認證:對所有的請求進行集中認證鑒權。
3)監控:記錄所有的API請求資料,API管理系統可以對API調用進行管理和性能監控。
4)限流熔斷:當流量過大時,可以在網關層做限流。當背景服務出現響應延時或者故障時,可以主動熔斷,保護後端的服務資源,同時,防止影響使用者體驗。
該架構看起來非常完美,有些類似于Spring Cloud标準架構,但它也存在一些問題。下面舉兩個例子。
1)有很多頁面需要顯示多個服務的資料。比如App首頁,它要根據使用者的不同來顯示不同的資訊。如果是門店營運人員,就要顯示工單數量、最近的工單、銷售訂單資料、最近待處理的訂單、低于庫存安全值的商品等。
2)很多時候,使用者的一個送出操作需要修改多個服務的資料。比如一個工單操作要修改庫存、銷售訂單狀态、工單的資料。
那麼,第一個問題出現了:這兩種情況要調用的接口做在哪個服務上?
接口設計過程中,經常需要糾結這個問題。當然,最終總能達成共識——第一個接口做在門店服務上,變成圖15-2所示的調用關系;
第二個接口做在工單服務上,變成圖15-3所示的調用關系。
• 圖15-2 門店服務接口
• 圖15-3 工單服務接口
接下來講第二個問題。因為這樣的需求非常多,是以服務經常會來回調用,最終服務調用關系就會變得糾纏不清,如圖15-4所示。
• 圖15-4 服務調用關系
這種複雜的依賴給疊代帶來了地獄般的感受,這一點在第12章中有詳細的描述,這裡不再贅述。
是以總結一下,目前要解決兩個問題。
1)對于很多頁面要用的接口,都要考慮放在哪個背景服務,這導緻決策效率低下,也導緻一些職責劃分不統一。
2)服務之間的依賴非常混亂。
為了解決這兩個問題,項目組決定抽象出一個API層。
API層
一般來說,用戶端的接口會有以下需求。
1)聚合:一個接口需要聚合多個背景服務傳回的資料,然後再傳回給用戶端。
2)分布式調用:一個接口可能需要依次調用多個背景服務,去修改多個背景服務的資料。
3)裝飾:一個接口需要重新裝飾一下背景傳回的資料,删除一些字段,或者對某些字段再加一個封裝,組成用戶端需要的資料。
項目組決定在用戶端和背景服務之間增加一個新的API層,專門來做這些事情,此時架構如圖15-5所示。
• 圖15-5 API層架構
所有的請求經過網關後都由一個共用的API層進行處理,這個API層沒有自己的資料庫,它做的事情就是去調用其他背景服務。
這樣的設計至少解決了兩個問題。
1)糾結某個接口該放在哪個服務的情況大幅減少了。如果是聚合、裝飾、分布式調用的邏輯,就都放在API層;如果是要落庫或者查詢資料庫的邏輯,就看目标資料放在哪個服務,資料在哪裡,邏輯就在哪裡。
2)背景服務之間的依賴也大幅減少了。目前的依賴關系隻有API層去調用各個背景服務,背景服務之間的調用關系減少了。
架構看起來更完美了一些,但是會面臨新的問題。
用戶端适配問題
一般來說,有一系列的接口給各種用戶端調用,比如App、H5、PC網頁、小程式等。正常來說,調用關系如圖15-6所示。
• 圖15-6 多種用戶端調用關系
但是,這樣的設計會有3個問題。
1)不同用戶端的頁面可能是不一樣的,比如App的功能比較多,就會要求頁面當中包含一些資訊;小程式要求比較輕量化,同樣的頁面就會少一些資料。這樣的問題會導緻背景服務的同一個API需要為不同的用戶端做不同的适配。
2)用戶端經常做一些輕微的改動,比如加一個字段、減一個字段。用戶端的接口都要求降低響應速度,為此需要遵循資料最小化原則。是以,伴随用戶端這些細微但頻繁的改動,背景服務也經常要釋出新版本。
3)結合1)和2),背景服務的版本釋出又要同時考慮不同用戶端的相容問題,無形中又增加了複雜度。
為了解決這些問題,可以考慮使用BFF。
BFF
(BackendforFront)BFF不是一個架構,而是一個設計模式。
它的主要理念是專門為前端設計優雅的背景服務(也就是API)。換句話說,就是每一種用戶端有自己的API服務。這樣調用關系就變成圖15-7所示。
• 圖15-7 使用BFF的調用關系
不同的用戶端請求經過同一個網關後會分别重定向到專門為這種用戶端設計的API服務(WX API即用于微信小程式的API)。
因為每個API服務隻針對一種用戶端,是以它們可以為特定的用戶端進行優化,使得邏輯更輕便,而且響應速度會比一個通用的API服務更快(因為不需要判斷不同用戶端的邏輯)。
另外,每種用戶端就可以自己釋出,而不需要跟其他的用戶端一起排期。
圖15-7中的架構是通用的,但還需要通過深入研究具體業務來完善。
這次項目所針對的系統非常龐大,整個業務鍊條所涉及的工作都包含在這個系統中。前面列出了6個服務,但實際上系統的服務有近百個,由幾百人組成的研發團隊在維護這個系統,分為新零售、供應鍊、财務、加盟商、售後、客服等幾個部門。
大家共同維護一個App,共同維護一個使用者界面,新零售、售後、加盟商、客服還有各自的小程式和H5。
為了解耦和分開排期,每個部門肯定會維護自己的API服務,App與PC前端也要按部門實作元件化,此時的調用關系如圖15-8所示。
• 圖15-8 元件化後使用BFF的調用關系
這個架構基本上就是每個部門都會維護自己的一系列API服務。
接下來展開讨論一些細節問題。
技術架構上怎麼實作
整套架構還是基于Spring Cloud實作,如圖15-9所示。主要的3層分别如下。
1)網關:網關使用Spring Cloud Zuul。Zuul拉取注冊到ZooKeeper的API服務,然後通過Feign調用API服務。
2)API服務:API服務是一個Spring Web服務。它沒有自己的資料庫,主要的邏輯就是聚合、分布式調用以及裝飾資料。它通過Feign調用背景服務。
3)背景服務:背景服務也是Spring Web服務,它有自己的資料庫和緩存。
• 圖15-9 基于Spring Cloud的分層架構
API之間的代碼重複怎麼解決
一般來說,H5、小程式之間的需求都是不一樣的。重複的代碼邏輯主要存在于PC和App的API,因為它們有些頁面功能是一樣的,隻不過布局不一樣。針對這一點,幾個部門有不一樣的邏輯。
1)有的部門是将這些重複的代碼放在一個JAR裡面,讓幾個API服務共用。
2)有的部門是将這些重複的代碼抽取在一個獨立的稱為CommonAPI的API服務中,其他API服務調用這個CommonAPI。
3)有的部門因為重複邏輯占少數,是以他們的做法就是保留這些重複代碼。根據他們的評估,維護這些重複代碼的成本會小于維護上述JAR或者CommonAPI服務的成本。如果有些API服務的出入參和背景服務提供接口的出入參一摸一樣,該怎麼辦?
針對這種情況就會使用API服務的接口,其實就是一個簡單的代理層,什麼事都不用做。
那這些僅為代理的API接口能不能直接去掉呢?如果需要,有幾個辦法可以實作。
1)網關可以繞過API服務,直接調用背景服務,但是這樣做就破壞了分層。
2)在API服務層做一個攔截器,如果這個URI找不到對應API服務中的controllermapping,就嘗試直接通過URI去找背景的服務,有的話就直接調用。
第一個辦法因為破壞了分層,很快就被否決了。項目組對第二個辦法争執了很久,最終的結論是,這樣做會增加系統的複雜度,出問題後調查起來很麻煩,而其好處隻是去掉了一些看起來有些累贅的代碼,從收益來說,并不會很大。而且這些代碼的編寫成本非常低,對整體的接口清單來說是可控的。綜合考慮後,項目組決定,不去掉這些接口代碼。
背景服務與API服務的開發團隊如何分工
最後的分工是這樣的:有一個專門的API團隊負責這些API服務,背景的服務再根據領域來劃分小組職責。
這樣做的好處就在于,API團隊對所有的服務有個整體的認識,由一個中心團隊控制接口的劃分,就不會出現背景服務劃分不清楚、服務重複的情況。
當然,壞處就在于API團隊整體業務邏輯偏簡單一些,無法讓人員長久在崗,是以也會定期進行崗位輪換。
小結
BFF這一章就講完了。本章并不是介紹一個技術方案,而是整體接口開發的管理和設計方案,是以其内容基本都是一些設計思路和具體會碰到的場景。
另外,雖然本章關于BFF的内容隻占一小部分,大部分是背景服務的分層設計,但是BFF的理念貫徹始終。
至此,微服務相關的架構已經講完了,接下來将會進入開發運維場景實戰,讨論如何讓開發更高效。
本文給大家講解的内容是微服務進階場景實戰:BFF,如何緩解服務依賴複雜度的問題?
- 下篇文章給大家講解的内容是開發運維場景實戰:接口Mock
- 覺得文章不錯的朋友可以轉發此文關注小編;
- 感謝大家的支援!!