關微信公衆號:網際網路架構師,在後台回覆 回復:2T,可以獲取我整理的教學,都是乾貨。
猜你喜歡
1、GitHub 標星 3.2w!史上最全技術人員面試手冊!FackBoo發起和總結
5、37歲程式設計師被裁,120天沒找到工作,無奈去小公司,結果懵了...
7、不認命,從10年流水線工人,到谷歌上班的程式媛,一位湖南妹子的勵志故事
原文:toutiao.com/i6833818331884028419
REST作爲一種現代網路應用非常流行的軟體架構風格,自從Roy Fielding博士在2000年他的博士論文中提出來到現在已經有了20年的歷史。它的簡單易用性,可延伸性,伸縮性受到廣大Web開發者的喜愛。
REST 的 API 配合JSON格式的數據交換,使得前後端分離、數據互動變得非常容易,而且也已經成爲了目前Web領域最受歡迎的軟體架構設計模式。
但隨着REST API的流行和發展,它的缺點也暴露了出來:
濫用REST介面,導致大量相似度很高(具有重複性)的API越來越冗餘。
對於前端而言:REST API粒度較粗,難以一次性符合前端的數據要求,前端需要分多次請求介面數據。增加了前端人員的工作量。
對於後端而言:前端需要的數據往往在不同的地方具有相似性,但卻又不同,比如針對同樣的使用者資訊,有的地方只需要使用者簡要資訊(比如頭像、暱稱),有些地方需要詳細的資訊,這就需要開發不同的介面來滿足這些需求。當這樣的相似但又不同的地方多的時候,就需要開發更多的介面來滿足前端的需要。增加了後端開發人員的工作量和重複度。
那我們來分析一下,當前端需求變化,涉及到改動舊需求時,會有以下這些情況:
做加法:
產品需求增加,頁面需要增加功能,數據也就相應的要增加顯示,那麼REST介面也需要做增加,這種無可厚非。
做減法:
產品需求減少,頁面需要減少功能,或者減少某些資訊顯示,那麼數據就要做減法。
一種通常懶惰的做法是,前端不與後端溝通,僅在前端對數據選擇性顯示。
因爲後端介面能夠滿足數據需要,僅僅是在做顯示的時候對數據進行了選擇性顯示,但介面的數據是存在冗餘的,這種情況一個是存在數據泄露風險,另外就是數據量過大時造成網路流量過大,頁面載入緩慢,使用者流量費白白消耗,使用者體驗就會下降。
另外一種做法就是告知後端,要麼開發新的介面,要麼,修改舊介面,刪掉冗餘欄位。
但一般來說,開發新介面往往是後端開發人員會選擇的方案,因爲這個方案對現有系統的影響最低,不會有額外的風險。
修改舊介面刪除冗餘數據的方案往往開發人員不會選擇,這是爲什麼呢?
這就涉及到了系統的穩定性問題了,舊介面往往不止是一個地方在用,很有可能很多頁面、設定不同用戶端、不同服務都呼叫了這個介面獲取數據,不做詳細的調查,是不可能知道到底舊介面被呼叫了多少次,一旦改動舊介面,涉及範圍可能非常大,往往會引起其他地方出現崩潰。改動舊介面成本太高,所以往往不會被採取。
同時做加減法:
既有加法,又有減法,其實這種就跟新需求沒啥區別,前端需要重做頁面,後端需要新寫介面滿足前端需要,但是舊介面還是不能輕舉妄動(除非確定只有這一處呼叫纔可以刪除)。
往往這個時候,其實用到的數據大多都是來自於同一個DO或者DTO,不過是在REST介面組裝數據時,用不同的VO來封裝不同欄位,或者,使用同樣的VO,組裝數據時做刪減。
看到這些問題是不是覺得令人頭大?
所以需求頻繁改動是萬惡之源,當產品小哥哥改動需求時,程式設計師小哥哥可能正提着鐵鍬趕來……
那麼有沒有一種方案或者框架,可以使得在用到同一個領域模型(DO或者DTO)的數據時,前端對於這個模型的數據欄位需求的改動,後端可以根據前端的改動和需要,自動適配,自動組裝需要的欄位,返回給前端呢?如果能這樣做的話,那麼後端程式猿小哥可能要開心死了,前端妹子也不用那麼苦口婆心地勸說後端小哥哥了。
所以GraphQL隆重出世了!
GraphQL是一種新的API標準,它提供了一種比REST更有效、更強大和更靈活的替代方案。
它是由Facebook開發並開源的,現在由來自世界各地的公司和個人組成的大型社羣維護。
GraphQL本質上是一種基於api的查詢語言,現在大多數應用程式都需要從伺服器中獲取數據,這些數據儲存可能儲存在數據庫中,API的職責是提供與應用程式需求相匹配的儲存數據的介面。
它是數據庫無關的,而且可以在使用API的任何環境中有效使用,我們可以理解爲GraphQL是基於API之上的一層封裝,目的是爲了更好,更靈活的適用於業務的需求變化。
簡單的來說,它:
它的工作模式是這樣子的:
REST API 的介面靈活性差、介面操作流程繁瑣,GraphQL 的宣告式數據獲取,使得介面數據精確返回,數據查詢流程簡潔,照顧了用戶端的靈活性。
用戶端拓展功能時要不斷編寫新介面(依賴於伺服器端),GraphQL 中一個服務僅暴露一個 GraphQL 層,消除了伺服器對數據格式的硬性規定,用戶端按需請求數據,可進行單獨維護和改進。
REST API 基於HTTP協定,不能靈活選擇網路協定,而傳輸層無關、數據庫技術無關使得 GraphQL 有更加靈活的技術棧選擇,能夠實現在網路協定層面優化應用。
舉個經典的例子:前端向後端請求一個book物件的數據及其作者資訊。
我用動圖來分別演示下REST和GraphQL是怎麼樣的一個過程。
先看REST API的做法:
再來看GraphQL是怎麼做的:
可以看出其中的區別:
與REST多個endpoint不同,每一個的 GraphQL 服務其實對外只提供了一個用於呼叫內部介面的端點,所有的請求都存取這個暴露出來的唯一端點。
GraphQL 實際上將多個 HTTP 請求聚合成了一個請求,將多個 restful 請求的資源變成了一個從根資源 POST 存取其他資源的 Comment 和 Author 的圖,多個請求變成了一個請求的不同欄位,從原有的分散式請求變成了集中式的請求,因此GraphQL又可以被看成是圖數據庫的形式。
那我們已經能看到GraphQL的先進性,接下來看看它是怎麼做的。
使用GraphQL介面設計獲取數據需要三步:
首先要設計數據模型,用來描述數據物件,它的作用可以看做是VO,用於告知GraphQL如何來描述定義的數據,爲下一步查詢返回做準備;
前端使用模式查詢語言(Schema)來描述需要請求的數據物件型別和具體需要的欄位(稱之爲宣告式數據獲取);
後端GraphQL通過前端傳過來的請求,根據需要,自動組裝數據欄位,返回給前端。
GraphQL的這種思考模式是不是完美解決了之前遇到的問題呢?!
總結它的好處:
在它的設計思想中,GraphQL 以圖的形式將整個 Web 服務中的資源展示出來,用戶端可以按照其需求自行呼叫,類似新增欄位的需求其實就不再需要後端多次修改了。
建立GraphQL伺服器的最終目標是:
允許查詢通過圖和節點的形式去獲取數據。
有人會問:
使用了GraphQL就要完全拋棄REST了嗎?
GraphQL需要直接對接數據庫嗎?
用GraphQL需要對現有的後端服務進行大刀闊斧的修改嗎?
答案是:NO!不需要!
它完全可以以一種不侵入的方式來部署,將它作爲前後端的中間服務,也就是,現在開始逐漸流行的 前端 —— 中端 —— 後端 的三層結構模式來部署!
那就來看一下這樣的部署模式圖:
也就是說,完全可以搭建一個GraphQL伺服器,專門來處理前端請求,並處理後端服務獲取的數據,重新進行組裝、篩選、過濾,將完美符合前端需要的數據返回。
新的開發需求可以直接就使用GraphQL服務來獲取數據了,以前已經上線的功能無需改動,還是使用原有請求呼叫REST介面的方式,最低程度的降低更換GraphQL帶來的技術成本問題!
如果沒有那麼多成本來支撐改造,那麼就不需要改造!
只有當原有需求發生變化,需要對原功能進行修改時,就可以換成GraphQL了。
下圖是一個 GraphQL 應用的基本架構,其中用戶端只和 GraphQL 層進行 API 互動,而 GraphQL 層再往後接入各種數據源。這樣一來,只要是數據源有的數據, GraphQL 層都可以讓用戶端按需獲取,不必專門再去定介面了。
一個GraphQL服務僅暴露一個 GraphQL Endpoint,可以按照業務來進行區分,部署多個GraphQL服務,分管不同的業務數據,這樣就可以避免單伺服器壓力過大的問題了。
宣告式數據獲取(可以對API進行查詢): 宣告式的數據查詢帶來了介面的精確返回,伺服器會按數據查詢的格式返回同樣結構的 JSON 數據、真正照顧了用戶端的靈活性。
一個微服務僅暴露一個 GraphQL 層:一個微服務只需暴露一個GraphQL endpoint,用戶端請求相應數據只通過該端點按需獲取,不需要再額外定義其他介面。
傳輸層無關、數據庫技術無關:帶來了更靈活的技術棧選擇,比如我們可以選擇對移動裝置友好的協定,將網路傳輸數據量最小化,實現在網路協定層面優化應用。
GraphQL對數據支援的操作有:
查詢(Query):獲取數據的基本查詢。
變更(Mutation):支援對數據的增刪改等操作。
訂閱(Subscription):用於監聽數據變動、並靠websocket等協定推播變動的訊息給對方。
要想要設計GraphQL的數據模型,用來描述你的業務數據,那麼就必須要有一套Schema語法來做支撐。
想要描述數據,就必須離不開數據型別的定義。所以GraphQL設計了一套Schema模式(可以理解爲語法),其中最重要的就是數據型別的定義和支援。
那麼型別(Type)就是模式(Schema)最核心的東西了。
什麼是型別?
對於數據模型的抽象是通過型別(Type)來描述的,每一個型別有若幹欄位(Field)組成,每個欄位又分別指向某個型別(Type)。這很像Java、C#中的類(Class)。
GraphQL的Type簡單可以分爲兩種,一種叫做Scalar Type(標量型別),另一種叫做Object Type(物件型別)。
那麼就分別來介紹下兩種型別。
標量是GraphQL型別系統中最小的顆粒。類似於Java、C#中的基本型別。
其中內建標量主要有:
String
Int
Float
Boolean
Enum
ID
上面的型別僅僅是GraphQL預設內建的型別,當然,爲了保證最大的靈活性,GraphQL還可以很靈活的自行建立標量型別。
僅有標量型別是不能滿足複雜抽象數據模型的需要,這時候我們可以使用物件型別。
通過物件模型來構建GraphQL中關於一個數據模型的形狀,同時還可以宣告各個模型之間的內在關聯(一對多、一對一或多對多)。
物件型別的定義可以參考下圖:
是不是很方便呢?我們可以像設計類圖一樣來設計GraphQL的物件模型。
那麼,型別系統僅僅只有型別定義是不夠的,我們還需要對型別進行更廣泛性的描述。
型別修飾符就是用來修飾型別,以達到額外的數據型別要求控制。
比如:
列表:[Type]
非空:Type!
列表非空:[Type]!
非空列表,列表內容型別非空:[Type!]!
在描述數據模型(模式Schema)時,就可以對欄位施加限制條件。
例如定義了一個名爲User的物件型別,並對其欄位進行定義和施加限制條件:
那麼,返回數據時,像下面 下麪這種情況就是不允許的:
Graphql會根據Schema Type來自動返回正確的數據:
除了上面的,Graphql還有一些其他型別來更好的引入物件導向的設計思想:
介面型別(Interfaces):其他物件型別實現介面必須包含介面所有的欄位,並具有相同的型別修飾符,纔算實現介面。
比如定義了一個介面型別:
那麼就可以實現該介面:
聯合型別(Union Types):聯合型別和介面十分相似,但是它並不指定型別之間的任何共同欄位。幾個物件型別共用一個聯合型別。
輸入型別(Input Types):更新數據時有用,與常規物件只有關鍵字修飾不一樣,常規物件時 type 修飾,輸入型別是 input 修飾。
比如定義了一個輸入型別:
前端發送變更請求時就可以使用(通過參數來指定輸入的型別):
所以,這樣物件導向的設計方式,真的對後端開發人員特別友好!而且前端MVVM框架流行以來,物件導向的設計思想也越來越流行,前端使用Graphql也會得心應手。
那麼,該怎麼設計來接入我們現有的系統中呢?
將Graphql服務直連數據庫的方式:最簡潔的設定,直接操作數據庫能減少中間環節的效能消耗。
整合現有服務的GraphQL層:這種設定適合於舊服務的改造,尤其是在涉及第三方服務時、依然可以通過原有介面進行互動。
直連數據庫和整合服務的混合模式:前兩種方式的混合。
可以說是非常靈活了!你都不用擔心會給你帶來任何的麻煩。
在伺服器端, GraphQL 伺服器可用任何可構建 Web 伺服器的語言實現。有以下語言的實現供參考:
C# / .NET
Clojure
Elixir
Erlang
Go
Groovy
Java
JavaScript
Julia
Kotlin
Perl
PHP
Python
R
Ruby
Rust
Scala
Swift
種類繁多,幾乎流行的語言都有支援。
在用戶端,Graphql Client目前有下面 下麪的語言支援:
C# / .NET
Clojurescript
Elm
Flutter
Go
Java / Android
JavaScript
Julia
Swift / Objective-C iOS
Python
R
覆蓋了衆多用戶端設計語言,而其他語言的支援也在推進中。
整理了下目前比較流行的服務架構:
Apollo Engine:一個用於監視 GraphQL 後端的效能和使用的服務。
Graphcool (github): 一個 BaaS(後端即服務),它爲你的應用程式提供了一個 GraphQL 後端,且具有用於管理數據庫和儲存數據的強大的 web ui。
Tipe (github): 一個 SaaS(軟體即服務)內容管理系統,允許你使用強大的編輯工具建立你 的內容,並通過 GraphQL 或 REST API 從任何地方存取它。
AWS AppSync:完全託管的 GraphQL 服務,包含實時訂閱、離線程式設計和同步、企業級安全特性以及細粒度的授權控制。
Hasura:一個 BaaS(後端即服務),允許你在 Postgres 上建立數據表、定義許可權並使用 GraphQL 介面查詢和操作。
Graphql的一些工具:
graphiql (npm): 一個互動式的執行於瀏覽器中的 GraphQL IDE。
Graphql Language Service: 一個用於構建 IDE 的 GraphQL 語言服務(診斷、自動完成等) 的介面。
quicktype (github): 在 TypeScript、Swift、golang、C#、C++ 等語言中爲 GraphQL 查 詢生成型別。
想要獲取更多關於Graphql的一些框架、工具,可以去awesome-graphql:一個神奇的社羣,維護一系列庫、資源等,地址是
https://github.com/chentsulin/awesome-graphql。
想要學習更多Graphql的知識,可以去GraphQL.cn。
關微信公衆號:網際網路架構師,在後台回覆 回復:2T,可以獲取我整理的教學,都是乾貨。
猜你喜歡
1、GitHub 標星 3.2w!史上最全技術人員面試手冊!FackBoo發起和總結
5、37歲程式設計師被裁,120天沒找到工作,無奈去小公司,結果懵了...
7、不認命,從10年流水線工人,到谷歌上班的程式媛,一位湖南妹子的勵志故事