閒魚在資料聚合上的探索與實踐
概述
隨著業務的不斷擴張,各種運營活動越來越多,原有的前端渲染-後端提供業務介面的開發方式對於一個生命週期可能只有幾天的活動來說成本巨大。閒魚在降低開發成本,提高整體效率上做了一些嘗試和實踐。本文介紹閒魚從資料聚合方面進行了一些探索和嘗試,以及Graphql的引入給閒魚帶了研發效率的提升。
背景
長期以來,前端和後端開發中面臨一個矛盾:前端希望頁面只獲取結構化資料,能夠直接渲染出頁面元件;後端則希望只提供業務領域API服務能力,資料組裝和處理由前端完成。mock資料,聯調等低價值的工作會耗費很多的成本,原有的開發模式已跟不上業務快速發展的節奏。 因此我們希望前端可以直接獲取資料,後端又能從重複的、低價值的消費型開發中解放出來。
資料聚合是我們解決的一個思路。
1. 資料聚合的解決方案
資料聚合是將多個服務請求一起打包給服務端,服務端一次性返回相應請求的結果,這種方式可以降低網路耗時,在資料處理上也會更方便。在入參語法上也有擴充套件的可能性,比如依賴呼叫等,是一種比RESTFul更加靈活和高效的查詢方式。
在資料聚合的呼叫下,由於服務端的業務領域介面已經存在,這些介面認為是可靠的,聯調成本將會大大降低,在一些測試環境發生異常的情形,前端甚至可以直接在線上測試。
設計原則
-
服務端暴露通用場景的資料服務,即標準業務API,包括資料查詢和寫入;
-
儘可能少與前端互動,一次呼叫獲取所有所需資料
-
併發/非同步呼叫降低耗時
2. 資料聚合1.0
閒魚服務端開發了第一個資料聚合服務。 通過將底層服務暴露出來,從請求總入口進行併發呼叫具體的服務介面,頁面多個服務查詢可以一次性將所有的資料返回給前端。 呼叫過程如下:
這個框架有如下幾個特點:
-
非常輕量,核心程式碼1000行左右
-
去中心化直接部署在應用系統上,不依賴其他二方包和服務系統,
-
無程式碼入侵,無需對現有系統服務和程式碼做改造適配,僅需在註冊中心註冊服務即可
-
全併發呼叫,呼叫的多個服務API均採用併發方式呼叫,耗時低 此外我們對其語法結構和功能上進行了擴充套件:支援欄位選取,依賴呼叫,迴圈依賴檢查,別名等功能:
2.1 上線效果
上線半年內,資料聚合服務支撐了30+的頁面上線,佔同類需求的80%以上,降低了兩端的開發成本超過50%。
2.2 閒魚聚合服務上線後存在以下問題:
-
資料響應結構對呼叫方不夠友好,雖然支援依賴呼叫,但是返回的資料是平級展現形式,對於一些批量介面來說,返回的結構往往是Map結構,這需要呼叫方進一步處理,增加了複雜度;
-
安全性問題。multiquery的查詢串沒有經過加密,一些非法的請求可能會修改查詢語句帶來系統風險;而且對於一些敏感資料需要加密或脫敏處理,multiquery語法結構上缺乏資料處理的擴充套件點
-
研發體系不完善:缺乏對服務的meta資訊透出,導致呼叫方不清楚要用哪個服務,入參是什麼出參是什麼,雙方存在一定的溝通成本。沒有ide支撐,書寫起來比較困難。
3. GraphQL-像寫sql一樣拼裝資料
3.1 什麼是Graphql
Graphql (https://graphql.org ) 是 facebook 推出的一種資料查詢語言,其設計的目的是要將不穩定的資料組裝部分從穩定的業務資料邏輯中剝離,使資料控制邏輯前移,開發模式由“下發資料”轉變成“取資料”的過程。
Graphql的優勢:
-
結構化清晰:所見即所得,輸入和輸出結構一致,前端需要什麼資料欄位,就在ql上填寫什麼欄位,同時支援多層級結構,也可以平級展現,由呼叫方根據業務決定合適的輸出形式。
-
精細化場景控制:即便是類似的場景,需要的資料也可能不完全相同,graphql中沒有一個數據是多餘的
-
資料處理可擴充套件性強:graphql提供了很多Directives滿足日常的開發需求,甚至支援js程式碼, 開發者也可以自定義一套工具庫來擴充套件
3.2 GraphQL接入應用改造
閒魚選擇了TQL作為Graphql的服務端實現。Tql是淘寶提供的對GraphQL的java實現,並解決了開源版graphql的很多侷限性和痛點,提供了很多特性,使graphql能夠低成本部署在應用上。
-
接入簡單,程式碼侵入性低:去中心化的設計,不依賴二方服務,應用系統直接引入可用
-
研發體系支撐:提供了ql編寫的線上ide,可展示各個服務的meta資訊,提高了開發效率,書寫提示,執行耗時日誌,呼叫場景監控等功能便於服務效能優化
-
多執行策略:提供了併發執行策略和非同步執行策略,在多服務呼叫和層級呼叫場景上保證了ql的高效執行;
-
合併呼叫:執行前合併所依賴的上游資料集中的元素,這樣我們就可以充分利用批量介面查詢,而不是單個多次呼叫,效能顯著提高
-
安全性提升: graphql語句從前端請求到服務端,用簽名的方式來避免查詢串被篡改
3.2.1服務端改造
-
統一graphql查詢介面
-
原有的一個業務領域服務再包裝一個GraphQL的Function
Function入參改造: 後臺的服務引數對於前端視角可能不同(引數過多,影響資料的引數等)
非批量Function出參:一般無需改造,同上
支援批量的Function改造,返回結構必須是有序List
非標準DO引數:由於輸出是JSON,存在迴圈依賴的java物件使用fastjson輸出時會造成棧溢位
開發GraphQL API
下面的示例可以提供一個Graphql的API。@GraphQLDataFetcher 註解表示該方法可以作為Graphql的資料來源,supportBatch = true表示支援合併呼叫,執行前會將依賴的資料結果合併成一個List作為入參;
支援合併呼叫情況下要求我們保證兩點:
-
入參userids的長度和返回的List長度一致,否則無法回填到相應的欄位上;
-
返回的資料順序要和入參的順序對應,否則會造成資料錯誤。
統一graphql Gateway API
執行時會自動引入當前請求的全域性資訊,如登入使用者,裝置id,裝置機型等,如UserAgent可獲取使用者的機型,系統等資訊,不需要前端和後端額外處理,可以直接使用。
3.2.2前端呼叫改造
-
呼叫介面改為後端提供的統一graphql介面
-
根據業務需求使用graphql語法表達所需資料
呼叫graphql gateway API:
編寫GraphQL 查詢語句,獲取結構化資料:
隨著前端團隊增多和人員流動,我們對Graphql的學習成本,ql複用以及管理上提出了更高的要求。
4. GraphqlQL管理平臺
管理平臺給開發者提供了很多便利。線上編編輯ql和儲存,線上除錯,即時釋出,多人可見,有示例參考,降低了graphql的學習成本。
前端在頁面請求時只需要傳入對應的Id,不再需要graphql查詢語句
GraphQL給閒魚帶來的變化
研發成本降低
引入Graphql後兩端的研發成本顯著降低,運營類場景整體上線時間明顯縮短,大部分情況下,服務端的成本為0。而前端在編寫graphql語句+自測的時間可能只有10分鐘。
GraphqlQL可以與前端頁面搭建平臺完美結合,如TMS,已經有很多頁面元件是基於GraphqlQL來完成的.藉助graphql可以很方便地構建出各種各樣的頁面元件,對研發無人化的方向上也有積極作用。
耗時
通過分析graphql的執行日誌,主要耗時在實際呼叫的介面耗時,graphql自身的耗時一般在20ms以下,某些情況下耗時較長。graphql耗時點包括:
-
ql複雜度
-
@js指令 後端執行js指令碼會引起較多耗時增加
-
合併呼叫時的入引數據處理與回填也有一定影響
5. 總結
本文介紹了閒魚在資料聚合上的一些探索和嘗試,並介紹了Graphql的引入和應用改造。從自研服務到Graphql的引入,研發效率不斷提升,並取得了很好的效果。 目前,graphql還只在weex/h5的場景上使用,將來我們會在native上使用並逐步擴大。
GraphqlQL的引入使前端/客戶端和服務端的程式設計模式發生了很大的改變。
-
服務端從此只需專注於建設穩定的業務領域模型,不再維護不穩定的、容易變化的VO層,也不需要與前端反覆溝通結構定義。
-
前端/客戶端 不再依賴服務端特定的介面,而是通過 graphql 來自由組合服務端提供各種資料服務,也可以更方便的進行頁面搭建,服務端基本不需要參與。
-
前端/客戶端 對業務模型也會有更深入的理解。