基於 GraphQL 實踐的一點思考

sea_ljf發表於2019-04-20

hello~親愛的觀眾老爺們大家好~最近一直沉迷於 GraphQL 的應用實踐,正好公司黑客馬拉松臨近,就拉上了兩位小夥伴,結合實際的業務場景,把 GraphQL 作為中間層的解決方案提上去~專案完成度還算不錯,對 GraphQL 也有了更深入的理解,在此記下整個過程的收穫。

注意~這篇文章不是學習 GraphQL 語法的文章,語法學習可左轉傳送門。但如若你對 GraphQL 有一定的瞭解,想知道實踐中可能碰到什麼問題,那估計本文能給你帶來一點幫助~以下是正文:

業務中的困局

既然要用 GraphQL,那還是要先了解它到底是什麼:

GraphQL 既是一種用於 API 的查詢語言也是一個滿足你資料查詢的執行時。 GraphQL 對你的 API 中的資料提供了一套易於理解的完整描述,使得客戶端能夠準確地獲得它需要的資料,而且沒有任何冗餘,也讓 API 更容易地隨著時間推移而演進,還能用於構建強大的開發者工具。

看起來沒什麼複雜的地方,也許你看過 GraphQL 的相關介紹,會認為它的語法很繁雜(這我倒是不否認),但歸根到底,它就是為了查詢而生。本質上就是丟一段東西給伺服器,伺服器解讀這段東西后,把對應的資料丟回來給你而已,所有的語法都為此而服務。

簡單介紹完 GraphQL,也要簡單介紹一下業務的背景~我們組真正的後端服務是用 C++ 進行編寫的,有一層很薄的 Go 作為閘道器與中間層,負責溝通頁端與 C++ 後端,同時提供解密、鑑權 等基礎服務。但由於業務的瓶頸不在前端(也導致話語權比較小),導致 C++ 後端經常表現得不那麼“業務”,也不認為為前端提供服務是工作的關鍵之一。導致他們對於很多後端能做與應該做的事情視而不見。後端隱含的態度基本是:“又不是不能用~”。但這對前端的程式碼質量與研發效率造成了負面的影響。

站在前端的角度,現在的系統存在以下問題:

  • 介面冗餘,影響頁端效能;
  • 資料體不規範,不同介面相同的 value,key 可能不一致;
  • 介面文件維護困難,文件中 key 的大小寫、value 的型別經常不一致,修改欄位後文件也存在滯後性;
  • 無法提供 Mock 服務。

由於各種原因,推動後端改變比較困難,那就自己重新架一套吧。新的中間層,使用 TypeScript 開發,選用了 Egg.js 為框架,魔改了 egg-graphql 這個外掛作為 GraphQL 服務的基礎。提供實時規範的介面文件、0 配置 Mock 服務、動態配置且熱更新等功能。

以下是功能與使用前後區別的一些截圖:

介面文件:

基於 GraphQL 實踐的一點思考

配置後臺:

基於 GraphQL 實踐的一點思考

頁端使用 GraphQL 之前:

基於 GraphQL 實踐的一點思考

頁端使用 GraphQL 之後:

基於 GraphQL 實踐的一點思考
(除了合併請求之外,不妨心算一下響應體大小區別)

新服務帶來的變化

相信大家對 GraphQL 的優點已瞭然於胸,通過上文對新的中間層的描述,也能感受到 GraphQL 的優點。這裡就不湊字數去詳述節省的頻寬、效能的提高及其它為前端帶來的其他便利。這裡主要想討論一下的是,歸根到底,我們是希望解放生產力,那麼 GraphQL 解放了什麼?

我們認為:GraphQL 能減少前後端矛盾,讓彼此更專注自身!

儘管上文吐槽了一把後端同學,但換個角度上,他們的做法無可厚非。介面單一、專注是合理的設計,冗餘的介面是因為版本與多端需要,Mock 服務更是後端無關的。無論前端還是後端,都是站在自己的立場去思考業務,並根據自身的技術為業務提供解決方案。但是業務是一個整體,程式設計師卻被分為了前後端。兩端中間的灰色區域,儘管能提高某一端的研發能力,但對另一端而言,卻是相當麻煩的。那麼:

All problems in computer science can be solved by another level of indirection.

既然灰色區域難於處理,那就再抽象一層!GraphQL 應運而生~它整合與提供了大量的功能,其中包括但不限於:文件服務、Mock 服務、單一入口、去冗餘、資料聚合等功能。解決了前後端的矛盾,後端可專注於自身,提供穩定而健壯的服務,不再關心前端 UI 所需資料及其意義;前端專注於頁面與互動,按需獲取對應資料,介面穩定、文件清晰。

個人認為,GraphQL 根本的優點是:讓各端更專注自身,更有效地解耦彼此。

陽光下的陰影

既然有光,那麼肯定會有影子,以下是勸退使用 GraphQL 的時間(笑)。由於我是一個相當怕麻煩又容易放棄的人,每當我碰到表現比現存系統更差或實現起來相當麻煩的時候,就會不自覺地思考新的技術是否適用於當前場景。以下是實踐過程的一些思考(已經明確的缺點:如快取、n+1 的問題等在此不再詳述):

中間層使用 GraphQL 可能是一個偽命題

GraphQL 很好,它清晰的標識了每個欄位的型別與含義,以此為基礎擴充套件出文件服務、Mock 服務、去冗餘等功能。但成也蕭何敗蕭何,作為中間層,一般是比較輕薄的,一般只是校驗與動態轉發內容即可,然而 GraphQL 其實相當重的。這導致了後端改欄位的時候,不單後端要發版,中間層也需要發版。為解決這個問題,我們想方設法對 GraphQL 服務進行動態配置及熱更新,儘管實現了這一功能,但裡面使用的都是 JavaScript 的黑魔法,如何遷移到其他語言(如 Go,公司在 Go 語言運用上已經比較成熟),說實話,我真不知道。而對於合併請求優化效能而言,GraphQL 也不是唯一的解決方案,Http2 一樣能解決問題~

BFF 中尷尬的處境

如果不將 GraphQL 作為中間層轉發的技術,而作為 BFF(Backend For Frontend)中請求的主要查詢語言,則會碰到一個比較尷尬的場景。在 BFF 中,前端掌握了伺服器,那麼介面的質量其實前端是能把握的。此時 GraphQL 只是一種選擇,它並沒有解決太多的問題,但它的學習與接入成本又不低。多少有點高不成低不就的感覺。

接入成本的評估

此處的接入成本,不單指編寫 GraphQL 相關程式碼的成本,還包括如何接入到公司現行系統的成本。由於 GraphQL 獲取資料的方式,存在瞬間請求大量資料的可能,很容易拖垮資料庫,然而請求的人卻對此一無所知。這有點像當初郵件系統剛出的時候,使用者往郵件中扔視訊檔案一樣,儘管是無意的行為,卻對系統造成極大的傷害。同時,誰去接也是一個值得商榷的問題。說到底,這是“爽”前端的技術,後端同學對此興趣乏乏。然而接入需要相關的開發與運維能力,前端同學不一定有足夠的能力去接,畢竟不是每一位同學都是全棧。其他的問題還有 rpc 接入、許可權控制等等。在方案落地之前,這些都需要仔細進行評估,不能盲目“追新”。

小結

綜上所述,GraphQL 可以為我們帶來很多便利,但它的接成本並不低。個人認為,GraphQL 並不是從根本上解決某個問題,而是在前後端之間幾個齷齪的地方進行了改進。在考慮是否使用 GraphQL 之前應進行多方面的考量。它比較適合以下的場景:

  1. 技術包袱比較小且業務不斷迭代。
  2. 業務介面十分複雜或多版本相容的場景(如報表系統、基礎的萬用介面等)。
  3. 介面細碎,但又對網路十分敏感的場景。

最後感謝 @Scott 相關的指導,儘管你給的建議每一條都用了其他方式去實現(笑)~但無疑是開闊了我們的眼界。

以上是個人的一點淺見,感謝各位看官大人看到這裡。知易行難,希望本文對你有所幫助~謝謝!

相關文章