摘要:我在工程實踐中直接使用類作為對應實體類的。因此我的結論是,此庫并不適用于我的工程實踐。工程實踐中對其應用方式的考慮在的官方教程中建議針對每請求創建新的實例,查詢請求結束則實例們的生命周期結束。
因為自己寫過基于react的前端應用,因此一看到GraphQL就被深深吸引,真是直擊痛點啊!
服務端開發一直是基于java, Spring的,因此開始研究如何在現有工程框架下加入graphql的支持。
本文屬于隨筆性質,學到哪里,用到哪里,就寫到哪里,觀點為個人理解,僅供參考。
Schema: 指一個特定GraphQL類型系統的定義,也指具體的包含類型系統定義的文本文件。在類型定義中,schema {...} 這樣的代碼塊定義的是入口類型,入口類型有三種,即查詢,變更和訂閱。值得說明的是,查詢,變更和訂閱也都是普通的類型而已,和其它對象類型語法上沒有任何區別,只不過它們作為入口類型被定義在schema代碼塊中。
查詢(query):定義為入口的對象類型;和變更、訂閱語法上并無不同,不過語義上對應的是讀操作。
變更(mutation): 定義和語法同上,但語義上對應增/刪/改操作。
訂閱(subscription): 定義和語法同上,語義上對應的是一個訂閱操作以及隨后服務器對客戶端的0~N次主動推送操作。
內省(introspection): 可以通過特殊的graphql查詢獲取到整個類型系統的詳細定義。這可能帶來數據模型過度暴露的問題,以后會專門說明。
類型(type): 沒什么好說,就是對象類型,和標量類型相對應。
標量(scalar): 非對象的簡單數據類型,比如內置的String, Int, ID等。可以自己定義新的標量類型,只要為它編寫序列化/反序列化方法即可,具體在graphql-java中對應的類是Coercing。
字段(field): 對象類型的成員,可以是對象類型或者標量類型。和java類里的field不同的是,GraphQL的field都是可以有參數的,因此有參數的field也可以理解成java中有特定類型返回值的方法。
接口(interface): 和java里的接口差不多,定義類型的公共字段,java實現中可以直接對應寫一個interface。有點麻煩的是在每個interface的實現類中都必須重復書寫公共字段。
聯合(union): 和接口類似,但是不要求任何公共字段。為了方便可以在java實現中使用無方法的interface實現。
片段(fragment): 這是個查詢時的概念,和schema定義無關,用于預定義類型上的若干個字段組合,后面的查詢語句中可以反復引用,可避免重復書寫這些字段組合。
內聯片段(inline fragment):片段還只是個簡化查詢的可有可無的東西,但內聯片段則更重要,對于返回interface或union類型的字段,需要使用內聯片段來根據結果的實際類型獲取不同的字段。
別名(alias): 在查詢中可為特定字段的查詢增加別名,用來在返回的結果中加以區分,比如一次查詢了兩個特定用戶,因為類型相同,字段也相同,如果不用別名,則無法在結果中區分彼此。
類型擴展(extend): 在schema中,可以使用extend給任意類型(包括interface/union)增加字段;這看似自找麻煩的機制實際上有很大用處,可以把高權限角色的特定字段使用extend寫在另外的schema文件中,運行時可合并解析,不同角色的用戶使用不同的schema。這樣可以通過加法來控制類型系統的可見性,避免內省機制過度暴露類型系統。
DataLoader: 用于批量查詢,見后文介紹。
Relay: Facebook的另一個框架,應該是基于GraphQL的,解決一些更高層的實際應用問題,比如通用的分頁機制等。
graphql-java特定術語DataFetcher: 數據獲取器,即用以獲取Field實際值的對象。
Data Class: 數據類,這是graphql-java-tools中的概念,對應schema中的同名對象類型。
可以在數據類上按照約定格式編寫DataFetcher方法用于獲取簡單字段值(比如無需另外查詢數據庫的字段)。
我在工程實踐中直接使用數據庫實體類作為數據類。
GraphQLResolver: 這是graphql-java-tools中的接口,帶有一個數據類的類型參數。
對該數據類定義部分或所有字段值的獲取方法,需要基于約定命名方法。
注意Resolver中的DataFetcher方法的優先級高于DataClass中的方法。
我在工程實踐中直接使用Dao類作為對應實體類的GraphQLResolver。
ExecutionInput: graphql-java中用來包裝一個完整查詢輸入的類,包括:
query - 查詢字符串;
operationName: 操作名; 可選;可用于在查詢中的多個操作中僅選擇特定名稱的予以執行。
variables: 變量; 可選;一個Map,用于替換查詢字符串中形如"$value"的變量。
context - 上下文; 可選;任意Object類型,會被傳遞給DataFetcher;可用于傳遞當前登錄用戶等。
root - 根對象; 可選;任意Object類型,會被傳遞給DataFetcher,語義上是被查詢的根對象。
ExecutionStrategy(執行策略): 定義查詢的具體執行策略。
比如是否異步執行,多個子查詢是依次執行,還是用線程池并發執行等。
Instrumentation(攔截器): 比較像Servlet容器中的Filter,在查詢執行前后各有一次執行機會。
可用于對輸入和結果進行額外處理;
支持鏈式執行;
需要指出的是DataLoader使用攔截器與核心系統耦合。
GraphqlFieldVisibility: 可以編程控制schema中各個字段的可見性。
和extend對應,相當于用減法來控制類型系統的可見性。
技術選型github上graphql-java名下的庫不少,如果希望了解各自簡介的,可以看下awesome-graphql-java這個項目。
我自己評估了以下幾個:
graphql-java: 這個是核心庫,完全符合Facebook的spec,可以直接解析schema文件,但是類型綁定需要使用RuntimeWiring來編程方式添加,用起來還是比較麻煩的。
graphql-java-annotations: 這是數據驅動的流派,使用注解直接在java類型上標注GraphQL類型以及DataFetcher等,不用寫schema文件。評估了一陣,個人感覺非常麻煩,比如:對每個字段都會創建新的DataFetcher實例來進行解析,十分低效;要編寫很多類來訪問不同字段;過多的對象直接創建,難以托管到Spring容器;等等。因此我的結論是,此庫并不適用于我的工程實踐。
graphql-java-tools: 這是Schema驅動的流派,這個庫使用Antlr自己重寫了Schema解析器,使用GraphQLResolver實例和Data Class;基于方法名和參數的約定來定義DataFetching,使用起來很方便。這是我最終選定使用的庫。不太爽的地方有兩點:1) 當前版本基于graphql-java 7.0,遲滯于核心庫 2) 使用Kotlin編寫,我在MyEclipse里面無法正常設置斷點進行跟蹤調試……
graphql-java-servlet: GraphQL不像傳統的REST,需要寫一堆Controller,提供唯一的api接口即可,這個servlet就是幫你連這個都包辦的,不過我沒有用,自己基于SpringMVC寫一個也很簡單。
批量數據查詢(解決N+1問題)graphql-java提供了兩種批量數據查詢的方案:
BatchedDataFetcher: 用起來挺簡單的,普通的DataFetcher是給你一個ID讓你返回一個對象,批量版是給你一個ID列表,讓你返回對應的對象列表。不過這個不是Facebook推薦的方式,在新版本中會廢棄掉。
DataLoader: 這個是Facebook官方推薦的方式,nodejs中的實現是基于js的異步機制延遲查詢,把最近一個周期產生的多個查詢集中執行(沒詳細了解,看文檔大概如此),java版實現方式則略有不同,下面詳細介紹。
關于DataLoadergraphql-java的dataloader是基于java8中新增的CompletableFeature類(大概相當于javascript里面的Promise),實現異步延遲批量獲取查詢結果。
大概原理(個人理解):
在DataFetcher方法中,并不直接返回實體類T,而是調用DataLoader.load()方法,返回一個CompletionStage
在查詢告一段落后(即能夠立即獲取的Field值都已取得,只剩下異步查詢未完成了),graphql-java會通過DataLoaderDispatcherInstrumentation.dispatch方法通知所有當前注冊的DataLoader去執行當前積壓的所有異步階段對象,具體就是會使用DataLoader對應的BatchLoader一次性查詢一批對象。
這時候又有一批Field的值已經實際取得,繼續按查詢的請求向下層展開,如果有新的異步階段對象產生,就繼續步驟2,直到所有異步階段對象都獲得最終值。
工程實踐中對其應用方式的考慮:
在graphql-java的官方教程中建議針對每請求創建新的DataLoader實例,查詢請求結束則DataLoader實例們的生命周期結束。
這個實現方式比較簡單,不用考慮緩存的更新問題,也不用考慮多個不同請求的緩存對象是否可共用。
舉個例子,張三和李四并發查詢張三的信息,他們獲取的"張三"用戶實例的結構可能是不同的,這種情況這兩個并發請求就不能共用緩存,而應該各自有獨立的DataLoader實例。
不過在我的工程實踐中,服務端內存中的數據實體類都是客觀一致的,其結構可見性應在更上一層即DataFetcher甚至Schema級別中進行過濾。
因此我的想法是為每種實體類維護單例的DataLoader,和Dao對象一一對應。
這種情況下,就不能簡單的使用DataLoader內部默認的簡單內存緩存了,因為此緩存是不會自動定時清理的。
graphql-java是允許開發者提供自己的緩存實現的,下一步我會結合項目中使用的Spring緩存管理器來具體實現。
graphql的查詢本身是有一定語法結構的特殊文本,對該文本進行解析也是有性能開銷的,因此graphql-java提供了緩存機制方便開發者把查詢文本的解析后數據結構緩存起來。
以下代碼引自官方教程,我準備結合我們項目里的EhCache來實作一下。
Cache關于訂閱的實現cache = Caffeine.newBuilder().maximumSize(10_000).build(); GraphQL graphQL = GraphQL.newGraphQL(StarWarsSchema.starWarsSchema) .preparsedDocumentProvider(cache::get) .build();
工程實踐中使用WebSocket實現訂閱。
無論是graphql還是graphql-java都未指定訂閱的具體實現機制,但WebSocket是現代瀏覽器普遍支持的,高性能低限制的服務器推送機制。
SpringMVC支持WebSocket,同時支持在低版本瀏覽器中使用Sock.js作為兼容備選方案。
另外,graphql-java體驗性支持的Defer數據獲取也可基于WebSocket實現。
基于spring和graphql-java-tools的寵物店例程
簡單的TODO例程,使用relay的思路解決分頁問題
基于WebSocket實現GraphQL訂閱
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69349.html
摘要:新聞熱點國內國外,前端最新動態就開源許可證風波進行回復數周前,基金會決定禁止旗下項目使用,因為其在標準的許可證之外添加了專利聲明此舉引發了社區的廣泛討論,希望能夠更新其開源許可證。 showImg(https://segmentfault.com/img/remote/1460000010777089); 前端每周清單第 27 期:React Patent License 回復,Sho...
摘要:新聞熱點國內國外,前端最新動態發布近日,正式發布新版本中提供了一系列的特性與問題修復。而近日正式發布,其能夠幫助開發者快速構建應用。 前端每周清單第 10 期:Firefox53、React VR發布、JS測試技術概述、Microsoft Edge現代DOM樹構建及性能之道 為InfoQ中文站特供稿件,首發地址為這里;如需轉載,請與InfoQ中文站聯系。從屬于筆者的 Web 前端入門...
摘要:前端每周清單第期微服務實踐,與,組件技巧,攻防作者王下邀月熊編輯徐川前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點分為新聞熱點開發教程工程實踐深度閱讀開源項目巔峰人生等欄目。 前端每周清單第 26 期:Node.js 微服務實踐,Vue.js 與 GraphQL,Angular 組件技巧,HeadlessChrome 攻防 作者:王下邀月熊 編輯:徐川...
摘要:異步剪貼板操作過去的數年中,各瀏覽器基本上都在使用來進行剪貼板交互。而提供了新的,則為我們提供了另一種異步式的剪貼板操作方式,本文即是對該機制與接口規范的詳細介紹。 showImg(https://segmentfault.com/img/remote/1460000013854167); 前端每周清單第 55 期: MobX 4 特性概覽,iOS Hacks 分享, 分布式事務詳解 ...
摘要:發布按照官方發布計劃,的發布意味著進入階段,徹底退出舞臺,的還有半年結束。為了應對這個挑戰,美團點評境外度假前端研發團隊自年月起啟動了面向端用戶的赫爾墨斯項目。前端技術越來越復雜,有不低的技術門檻。 推薦 1. 利用 Dawn 工程化工具實踐 MobX 數據流管理方案 https://zhuanlan.zhihu.com/p/... 項目在最初應用 MobX 時,對較為復雜的多人協作項...
閱讀 1989·2021-11-22 14:45
閱讀 2606·2021-10-12 10:11
閱讀 773·2021-09-22 10:02
閱讀 1216·2019-08-30 15:55
閱讀 1145·2019-08-30 15:54
閱讀 3256·2019-08-30 15:54
閱讀 1190·2019-08-29 17:16
閱讀 3088·2019-08-28 17:55