本文首發於個人微信公眾號:coder小黑
服務拆分的痛
服務拆分之后,前后端同學之間關於 API 粒度的爭吵越來越常見:
「前端同學請求兩個接口,聚合一下數據不就行了?」后端同學想只提供業務領域基礎 API 服務能力,數據組裝處理則希望由前端同學完成。
「后端聚合一下,前端可以少一次請求,只負責頁面渲染!」前端同學希望只負責頁面渲染,而 H5、APP、小程序同一個聚合邏輯可能會出現在三端,后端聚合則只需要一次。
接口聚合服務就是我們的一個解決思路。
接口聚合服務是什么?
接口聚合服務就是一個搬運工,只是幫助前端同學聚合多個接口的返回數據,聚合之后一次性返回相應請求的結果給客戶端。我們希望通過接口聚合服務這個中間層,做到可以讓前端直接獲取數據,而后端也能繼續專心於提供基礎業務領域 API 服務能力。
場景分析
- 場景一:串行獲取數據。多個請求,有關聯關系。
- 例如:通過商品 ID 獲取評論信息,通過評論中的 uid 獲取用戶信息
- 場景二:並行獲取數據。多個請求,無關聯關系。
- 例如:通過商品 ID 獲取商品信息、獲取商品活動信息、獲取當前用戶已購信息
方案調研
方案 A | 方案 B | |
---|---|---|
調用者 | 客戶端 | 客戶端 |
API-Server | 自研 | GraphQL |
Payload | 約定 | GraphQL |
Response | 約定 | GraphQL |
容錯 | 約定 | 約定 |
動態選取字段 | 是 | 是 |
最終我們選擇了方案 A,通過自研一套簡單的接口聚合中間層來解決這個問題。
於是,就有了接口聚合服務:api-aggregator。該框架有如下幾個特點:
-
核心代碼在千行左右,輕量級實現。
-
對現有代碼無侵入性,無需對現有服務和代碼做改造適配,現有接口可直接使用。
-
提供了 ApiAggregatePostProcessor 拓展點來干預接口聚合的各個階段,可拓展性強。
-
對前端友好,前端同學可以自定義返回數據結構,支持字段動態選取。
-
接口聚合邏輯直接通過配置文件和 api-aggregator 交流,新增聚合接口,無需發布。
api-aggregator:接口聚合服務
api-aggregator 認為一個聚合接口應該是由若干個接口的返回結果聚合而成的,因此在設計時,我們將其被划分為兩個部分:接口元信息和接口之間的聚合邏輯。
ApiDefinition:接口元信息
ApiDefinition 不僅定義了接口的元信息,同時也描述了接口所需參數的來源。
api-aggregator 認為在一次接口聚合中,元信息接口的參數可能有以下一些來源:
- 直接由客戶端傳遞過來,即直接從 HttpRequest 中獲取參數。
- 從上個接口的返回值中獲取。例如:通過商品 ID 獲取評論信息,通過評論中的 uid 獲取用戶信息。此時 uid 參數就需要從上個接口的返回值中獲取。
ResponseDefinition:接口間聚合邏輯
ResponseDefinition 描述了接口間的聚合邏輯,通過 ResponseDefinition 前端同學可以自定義接口返回的數據結構,也可以動態選取所需字段。
如果沒有 ResponseDefinition,則 api-aggregator 只能簡單的將兩個接口的數據平級的聚合在一起(如上左圖所示)。而現在,可以通過 ResponseDefinition 來定義返回結構體,給前端同學更好的開發體驗(如上右圖所示)。
簡單聊聊設計
配置文件預加載
接口聚合配置信息是由前端開發同學在管理后台配置的。
前端同學在提交配置文件之后,api-aggregator 就會對配置文件做一些靜態分析:分析接口的依賴情況,是否存在循環依賴等問題。
為了提高性能,api-aggregator 將相關的配置信息解析好之后,會直接緩存在內存中,以減少對同一份配置文件的反復解析,同時,再通過定時刷新和 MQ 的 pub/sub 來保證數據的一致性。
簡化 http 請求模型
api-aggregator 抽象了 HttpMethodInvoker 來發起 HTTP 請求。通過 Supplier 來獲取返回結果,屏蔽了不同 Http Client 之間的 API 差異。
還記得前文提到的場景嗎?
場景一:串行獲取數據。多個請求,有關聯關系。
場景二:並行獲取數據。多個請求,無關聯關系。
在 api-aggregator 中,將這兩個場景進行了簡化合一。
首先, api-aggregator 在解析配置文件分析接口依賴時,會根據接口的依賴情況給出一個 api-aggregator 認為是最優的 HTTP 請求流程,而不是根據配置文件定義的接口順序依次請求。
舉個例子:
假設在一次接口聚合中,需要請求接口 A、B、C,而接口 B 的數據依賴於接口 A,接口 A 和接口 C 的請求參數均可直接從 HttpRequest 中獲取參數。
那么,在實際的接口聚合過程中,api-aggregator 會先請求接口 A 和接口 C,然后阻塞獲取接口 A 的返回結果,最后請求接口 B。
提供擴展點
api-aggregator 提供了 ApiAggregatePostProcessor 來方便后續擴展。
通過 ApiAggregatePostProcessor,api-aggregator 可以干預一個接口聚合的整個流程,例如:緩存接口信息、增加監控日志等等。
雖然通過 ApiAggregatePostProcessor 可以來干預接口的聚合流程,但是想要添加新的 Processor 時還是需要重啟api-aggregator。而 api-aggregator 作為接口聚合點,和網關相似,也是流量的集中點,在后續的版本中,可能會考慮引入 Groovy 腳本,來支持動態的開啟和停用 Processor。
最后,歡迎大家在評論區一起留言討論,感謝你的閱讀~~
歡迎關注個人公眾號: