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