由中間層引起的關於GraphQL的思考

1、中間層引起的思考

你們都知道咱們目前應用的統一技術架構是先後端分離的形式,可是在實際開發中,二者之間在數據結構、數據交互方案、業務邏輯等等方面老是存在各類各樣的矛盾,彷佛必定要有一方在設計或實現上向另外一方妥協。好比一個頁面在初始化時,前端不得不調用多個接口來獲取多個微服務提供的業務數據,由於後端但願在微服務之間減小互相調用達到彼此解耦的狀態;再好比一個接口既有WEB端請求的需求也有APP端請求的需求,不可避免的就會出現一大堆冗餘數據是咱們不想看見的。前端

因此你們或多或少應該都思考過中間層的問題:也許咱們能夠把這些先後端之間的矛盾化解在中間層裏。web

圖片描述

在思考這個問題的時候,咱們發現了一種全新的定義API的方式——GraphQL。傳統的web API大概就是REST(表述性狀態傳遞,Representational State Transfer),發送請求到一個特定的URL,而後你會收到各類結果,好比HTML,XML,JSON,PDF,JPEG…等等這些web能夠理解的任一格式。可是GraphQl提出一種新的理念,若是前端開發能夠自由定義請求和響應,若是前端開發能夠精確地指定我要從API獲取那些數據,是否是後端的接口設計只須要考慮業務模型作最正宗的RESTFUL API了呢?後端

這樣理想化的只能出如今想象中的解決方案,不管是否能做爲適合咱們技術架構的中間層方案,彷佛都值得咱們深刻了解一下。api

2、GraphQL究竟是什麼

圖片描述

「一切皆是圖:使用 GraphQL,你能夠將你全部的業務建模爲圖」數組

這是官方的自身定義,其實GraphQL這個名字,Graph + Query Language,就代表了它的設計初衷是想要用相似圖的方式表示數據。接下來會重點介紹一下GraphQL的「繪圖」方式,請你們不要糾結於下面的語法,由於本文的目的不是讓你們徹底掌握GraphQL的語法,而是理解它的運行機制以及核心概念。瀏覽器

先來看一組常規的REST接口是如何設計的:緩存

GET /api/v1/products/
GET /api/v1/product/:id/
POST /api/v1/product/
DELETE /api/v1/product/:id/
PATCH /api/v1/product/:id/

假設咱們要在前端展現一個商品庫,以上五個接口就是很是標準的請求地址定義,後臺返回咱們提早商議好的數據類型便可,那麼GraphQL如何「繪製」這些接口呢?服務器

query {
  product(): [Product!]!
  product(id: Int): Product!
}

mutation {
  createProduct(): Product!
  updateProduct(id: Int): Product!
  deleteProduct(id: Int): Product!
}

第一次接觸看起來有點陌生,好的,咱們來解釋兩個概念:Schema 和 Tpye Modifier網絡

1.Schema 數據結構

Schema是用來描述接口獲取數據的邏輯的,咱們能夠簡單的理解爲REST架構中每一個獨立資源的URI,只不過在GraphQL中,是經過<查詢>來描述資源的獲取方式,Schema就是多個<查詢>組成的一張表。(本文把<查詢>作了特殊標記,但願明確它是屬於GraphQL的一種特殊行爲,區別於其餘語義)

上面的商品庫案例中,兩個特殊標記query和mutation就是<查詢>的其中兩種類型,也是咱們在項目中最經常使用的兩種,還有第三種類型是爲了長連接提出的subscription類型。三種類型對應不一樣的<查詢>場景:

  • query(查詢):當獲取數據時,應選取Query類型
  • mutation(更改):當嘗試修改數據時,應使用mutation類型
  • subscription(訂閱):當但願數據變動時,能夠向客戶端進行消息推送時,使用subscription類型

很明顯,獲取商品庫列表和單個商品詳情屬於query類型,新增、修改、刪除商品屬於mutation類型,咱們好像讀懂了一點上面的案例。第三種subscription類型是經過聲明式的定義創建一個長連接,來獲取服務端的推送消息,這裏不展開。

2.Tpye Modifier

在GraphQL中有兩種類型修飾符,分別是List和Required,他們在案例中體現分別是[Product]和Product!,[Product]表示它的類型是一個元素爲Product的數組,Product!表示該字段爲必須返回的字段。細心地小夥伴會發現案例中二者能夠結合使用,那麼關於 [Product]! 和 [Product!] 和 [Product!]! (注意 ! 的位置)的區別爲:

  • 列表自己爲必填項,但其內部元素能夠爲空
  • 列表自己能夠爲空,可是當列表存在時其內部元素爲必填
  • 列表自己和內部元素均爲必填

瞭解了Schema 和 Tpye Modifier以後,再來看一下GraphQL對商品庫幾個接口的定義

query {
  products(): [Product!]!
  product(id: Int): Product!
}

mutation {
  createProduct(): Product!
  updateProduct(id: Int): Product!
  deleteProduct(id: Int): Product!
}

有沒有以爲清晰簡潔一目瞭然呢?可是這隻完成了一半的工做,別忘了咱們真正要作的是自定義返回數據啊!上面的<查詢>中,到底會返回什麼樣的數據呢?咱們須要再瞭解兩個新的概念:Resolver 和 Type。

3.Resolver (解析函數)和 Type

咱們在<查詢>中聲明瞭products(): [Product!]!,那麼這個<查詢>對應的解析函數名必然叫products,這樣才能對應起來,這個解析函數的聲明過程以下:

Query {
  products {
       id
       name
       price
       classify {
           id
           name
       }
   }
}

這裏就應用了兩種簡單的Type,標量類型和對象類型,很明顯id、name、price就屬於標量類型,classify就屬於對象類型,classify中的id和name屬於標量類型,這些應該很好理解。

GraphQL在解析這段查詢語句時會按以下步驟:

  1. 首先進行第一層解析,當前<查詢>的類型是query,同時它的名字是products;以後會嘗試使用products的解析函數獲取解析數據,第一層解析完畢。
  2. 對第一層解析的返回值,進行第二層解析,當前products還包含四個子Query,分別是id、name、price和classify:id、name、price爲標量類型,解析結束;classify爲對象類型,嘗試再次獲取數據,第二層解析完畢。
  3. 對第二層解析的返回值,進行第三層解析,當前classify還包含一個id、 name,因爲它們是標量類型,解析結束。

能夠發現,GraphQL大致的解析流程就是遇到一個<查詢>以後,嘗試使用它的解析函數取值,以後再對返回值進行解析,這個過程是遞歸的,直到所解析的類型是標量類型爲止,整個過程咱們能夠把它想象成一個很長的解析鏈。

以上只是對GraphQL的運行機制以及核心概念進行最簡單的介紹,若是你們感興趣的話,能夠到官網學習更詳細的API。

圖片描述

3、GraphQL能幫咱們作什麼

1.請求冗餘和數據冗餘:
文章開頭提出的兩種矛盾,就是請求和數據的冗餘問題,均可以經過GraphQL解決。

2.靈活且強類型的Schema:
GraphQL的<查詢>具備明確聲明特性是它最顯著地特色。查詢和返回數據有個明肯定義不僅是爲了與API和實現方面保持一致,同時能夠做爲接口校驗的一種方式。

3.下降接口變更的維護成本:
REST中,一旦要改動API,無論是增刪值域,改變值域範圍,仍是增減API數量,改變API url,都很容易變成傷筋動骨的行爲。GraphQL就輕鬆多了。GraphQL的Service,API endpoint極可能就只有一個,根本不太會有改動URL path的狀況。至始至終,數據的請求方都只須要說明本身須要什麼內容,而不須要關心後端的任何表述和實現。數據提供方,好比server,只要提供的數據是請求方的母集,不論它們各自怎麼變,都不須要由於對方牽一髮而動全身。

4.提高應用程序性能:
服務器雖然一樣須要調用與客戶端相同的服務和REST API。可是大多數數據是在同一數據中心內的服務器之間流動。這些服務器到服務器之間的調用延遲很是低,並且帶寬很是高,與瀏覽器的網絡調用相比,性能提高是很是顯著的。

目前GraphQL的應用已經日益普遍了

圖片描述

總的來講GraphQL最大的優點,就是它可以大大提升開發者的效率,並且最大化地簡化了前端的數據層的複雜性,而且使先後端對數據的組織觀點一致。只是真正要用的話,須要考慮遷移成本、性能、緩存等等太多方面的要求,但願有一天咱們能夠真正去思考如何應用GraphQL。

參考資料:

1.官方文檔:https://graphql.cn/
2.網飛的實戰經驗:https://baijiahao.baidu.com/s...

相關文章
相關標籤/搜索