[譯] 關於使用 GRAPHQL 構建項目的回顧

在 2016 年底,咱們決定用 Python 和 React 重寫老舊的 PHP 遺留系統。因爲只有四個月的時間在 2017 年的節日(到來前)及時創建 MVP(模式開發的系統),咱們必須很是謹慎地決定如何投入時間。前端

咱們投入使用的技術之一就是 GraphQL。咱們中以前還歷來沒有人用過它,但咱們認爲它對於快速交付以及能讓人們獨立工做相當重要。python

事實證實這是一個很是好的決定,因此兩年後咱們想回顧並分享從那時起學到的東西...react

兩年後的 GraphQL

咱們從遺留系統學到的教訓,大大影響了咱們,因而咱們決定使用 GraphQL。咱們在至關數量的微服務之間使用 REST APIS,致使不少混亂,如不兼容的接口,不一樣的資源標識符和很是複雜的部署。任何 API 的變更都須要同時部署全部使用了這個 API 的服務以免停機故障,這會常常出現錯誤並致使很長的發佈週期。在單個 API 網關使用 GraphQL,咱們將能夠大大簡化服務格局。咱們也決定了使用 Relay,它爲咱們提供了一種識別資源的單一的、全局的方式,以及組織 GraphQL 模型的簡單方法。android

咱們使用單一服務做爲 GraphQL 服務器,它反過來會請求各類後端服務 -- 其中大部分是 REST APIs,可是由於它們都只和網關通訊,因此它們可使用任何想用的接口。網關被設計爲徹底無狀態的,這對於可擴展性大有裨益。緩存也是在 GraphQL 網關中,所以,只需擴大網關實例的數量,就能夠輕鬆擴展整個系統。ios

API 網關並非 GraphQL 世界的規章,因此爲何儘管使用它們意味着須要從網關到後臺服務的附加請求,咱們還要使用呢?對於咱們而言,最大的緣由就是減小 API 的相互依賴。沒有網關的話,咱們的服務結構將會差很少像這樣:git

不少服務都和不少其餘服務互通,致使須要大量的 API 鏈接,鏈接數量會以大體至關於服務數量的二次方的速度增加。這不但幾乎不可能讓任何人記住,同時還在處理中斷,維護和 API 更改時,增長了大量複雜性。github

即便在這樣的網絡中,GraphQL 也可以幫助提高向後兼容性,但這是當你在服務之間放置一個單一網關的時候所會發生的事情:web

突然間,就僅有線性數量的鏈接了,每一個新的服務僅會在網絡圖中增長一個新鏈接。API 變化僅須要影響它的源服務和網關。數據庫

API 網關是服務互相通訊的惟一途徑,這就大大下降了複雜度。它還爲緩存,縮放,監視和分析建立了一個很好的中心位置。通常來講,只有一項服務負責這麼多事情並非一個好主意 -- 而是一個故障點。django

可是,API 網關是無狀態的。它沒有數據庫,沒有本地資源也沒有認證。這意味着它能夠在水平方向上縮放自如,同時由於它還負責了緩存,因此僅增長網關實例的數量就有助於顯著解決流量高峯(的問題)。

固然,網關也不是全無代價的:一個請求如今要發送兩次了,而且若是一個後臺服務想要和另外一個後臺服務通訊,就必須經過網關。這對於建立一個更易於維護的中心接口很是有用,可是對於性能來講並非很好。這就是無狀態網關展示本身光輝的時候。由於網關代碼在哪裏運行並不重要,那就沒有什麼能阻止咱們將每一個後端服務都做爲其本身的網關。咱們將 GraphQL 接口移動到了每一個服務中,直接發送網絡請求,而不是發送兩次,這樣徹底不須要使用 GraphQL 服務器,可是卻依舊保留了全部 GraphQL 中心模型的優點。而且因爲咱們使用了 Python 定義了 GraphQL 模型,咱們決定更深一步,經過從 GraphQL 模型中自動生成 API 包裝器,能夠直接在 Python 中使用它。

結果就是如今服務間的通訊代碼變成了以下這樣:

GraphQL Code

GraphQL 模型的 API 包裹器是徹底從 graphene schema 自動生成的。因此,服務甚至不須要模型文件的副本。沒有多餘的請求,身份驗證在後臺透明處理,字段在訪問時會被延遲解析。

如今,在這樣的環境中,成爲一個好的 API 「公民」就會有一些要求了。後臺 API 大多能夠作任何它們想作的事情,可是在如何進行緩存和權限檢查的時候它們必須發揮很好的做用。咱們在後端 APIs 中使用的規則以下:

避免嵌套對象,僅返回相關聯對象的 IDs

在 REST API 中返回嵌套的對象是減小請求數量的一個很好的方法。可是這也讓緩存很是困難,並可能致使獲取多餘的資源,這正是 GraphQL 應該對抗的。一般狀況下,咱們避免大的,複雜的請求,而更偏向於稍多可是容易緩存的,更加扁平化的請求。

若是確實須要嵌套,毫不嵌套那些有附加權限的對象

有時候性能要求超過了簡單性的要求,那咱們就能夠返回潛逃的對象,例如,在一個 API 應答中包含一個相關聯的嵌套對象的長列表。可是,咱們只在被嵌套對象的權限不比外層對象更嚴格的狀況下這樣作,由於若是不這樣,應答就沒法被緩存。

咱們使用 graphene 和 graphene-django 來實際運行服務器,咱們不使用 graphene-django 自動映射 Django 模型的能力,由於全部的數據都來自外部請求,咱們只使用它來與咱們的堆棧的其他部分兼容並熟悉它。整個網關服務實際上就是一個單獨的 GraphQLView,咱們作了一點小小的擴充來容許咱們對前端作出優化:

  • 咱們將報錯信息優化,用以將 Django REST Framework 錯誤從後端服務中分解出來。DRF 每一個字段可能有不止一個錯誤,但它在原生 graphene-django 中並不起做用,因此咱們擴展了視圖,用來爲每一個字段提供精確的錯誤信息。
  • 咱們擴展了錯誤日誌,以便更容易地報告各類錯誤信息。例如 4xx 錯誤實際意味着用戶錯誤,可是因爲網關調用了另外一個不一樣的 API,它一樣也意味着網關錯誤的使用了咱們的 API。DRF 不會記錄後臺服務的 4xx 錯誤,所以,當其實是咱們而不是用戶致使的錯誤時,咱們會在網關中執行此操做。
  • 監控:GraphQLView 是添加各類性能監控位的絕佳位置。咱們追蹤每一個請求的執行時間,對查詢進行散列,以便合計不一樣參數的同一請求的響應時間。

GraphQL 對咱們大有益處,可是咱們也犯了不少錯誤。有時候咱們會努力保持咱們的 API 能真正向後兼容,除了性能監控和更好的錯誤報告,還必須爲已棄用的字段投入額外的監控。每次 API 變化就須要手動更新 GraphQL 模型,這是至關乏味的事情;而且爲了經過 GraphQL 使後臺服務的通訊變得很是容易,咱們有時也會打破一些服務的邊界。但最終,它幫助咱們更快地發展,維持了基礎設施核心模型的簡易,並使咱們的團隊更加自動化。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索