成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

GraphQL java工程化實(shí)踐

MSchumi / 2436人閱讀

摘要:我在工程實(shí)踐中直接使用類作為對(duì)應(yīng)實(shí)體類的。因此我的結(jié)論是,此庫(kù)并不適用于我的工程實(shí)踐。工程實(shí)踐中對(duì)其應(yīng)用方式的考慮在的官方教程中建議針對(duì)每請(qǐng)求創(chuàng)建新的實(shí)例,查詢請(qǐng)求結(jié)束則實(shí)例們的生命周期結(jié)束。

因?yàn)樽约簩?xiě)過(guò)基于react的前端應(yīng)用,因此一看到GraphQL就被深深吸引,真是直擊痛點(diǎn)?。?br>服務(wù)端開(kāi)發(fā)一直是基于java, Spring的,因此開(kāi)始研究如何在現(xiàn)有工程框架下加入graphql的支持。
本文屬于隨筆性質(zhì),學(xué)到哪里,用到哪里,就寫(xiě)到哪里,觀點(diǎn)為個(gè)人理解,僅供參考。

GraphQL基本概念

Schema: 指一個(gè)特定GraphQL類型系統(tǒng)的定義,也指具體的包含類型系統(tǒng)定義的文本文件。在類型定義中,schema {...} 這樣的代碼塊定義的是入口類型,入口類型有三種,即查詢,變更和訂閱。值得說(shuō)明的是,查詢,變更和訂閱也都是普通的類型而已,和其它對(duì)象類型語(yǔ)法上沒(méi)有任何區(qū)別,只不過(guò)它們作為入口類型被定義在schema代碼塊中。

查詢(query):定義為入口的對(duì)象類型;和變更、訂閱語(yǔ)法上并無(wú)不同,不過(guò)語(yǔ)義上對(duì)應(yīng)的是讀操作。

變更(mutation): 定義和語(yǔ)法同上,但語(yǔ)義上對(duì)應(yīng)增/刪/改操作。

訂閱(subscription): 定義和語(yǔ)法同上,語(yǔ)義上對(duì)應(yīng)的是一個(gè)訂閱操作以及隨后服務(wù)器對(duì)客戶端的0~N次主動(dòng)推送操作。

內(nèi)?。╥ntrospection): 可以通過(guò)特殊的graphql查詢獲取到整個(gè)類型系統(tǒng)的詳細(xì)定義。這可能帶來(lái)數(shù)據(jù)模型過(guò)度暴露的問(wèn)題,以后會(huì)專門說(shuō)明。

類型(type): 沒(méi)什么好說(shuō),就是對(duì)象類型,和標(biāo)量類型相對(duì)應(yīng)。

標(biāo)量(scalar): 非對(duì)象的簡(jiǎn)單數(shù)據(jù)類型,比如內(nèi)置的String, Int, ID等。可以自己定義新的標(biāo)量類型,只要為它編寫(xiě)序列化/反序列化方法即可,具體在graphql-java中對(duì)應(yīng)的類是Coercing。

字段(field): 對(duì)象類型的成員,可以是對(duì)象類型或者標(biāo)量類型。和java類里的field不同的是,GraphQL的field都是可以有參數(shù)的,因此有參數(shù)的field也可以理解成java中有特定類型返回值的方法。

接口(interface): 和java里的接口差不多,定義類型的公共字段,java實(shí)現(xiàn)中可以直接對(duì)應(yīng)寫(xiě)一個(gè)interface。有點(diǎn)麻煩的是在每個(gè)interface的實(shí)現(xiàn)類中都必須重復(fù)書(shū)寫(xiě)公共字段。

聯(lián)合(union): 和接口類似,但是不要求任何公共字段。為了方便可以在java實(shí)現(xiàn)中使用無(wú)方法的interface實(shí)現(xiàn)。

片段(fragment): 這是個(gè)查詢時(shí)的概念,和schema定義無(wú)關(guān),用于預(yù)定義類型上的若干個(gè)字段組合,后面的查詢語(yǔ)句中可以反復(fù)引用,可避免重復(fù)書(shū)寫(xiě)這些字段組合。

內(nèi)聯(lián)片段(inline fragment):片段還只是個(gè)簡(jiǎn)化查詢的可有可無(wú)的東西,但內(nèi)聯(lián)片段則更重要,對(duì)于返回interface或union類型的字段,需要使用內(nèi)聯(lián)片段來(lái)根據(jù)結(jié)果的實(shí)際類型獲取不同的字段。

別名(alias): 在查詢中可為特定字段的查詢?cè)黾觿e名,用來(lái)在返回的結(jié)果中加以區(qū)分,比如一次查詢了兩個(gè)特定用戶,因?yàn)轭愋拖嗤?,字段也相同,如果不用別名,則無(wú)法在結(jié)果中區(qū)分彼此。

類型擴(kuò)展(extend): 在schema中,可以使用extend給任意類型(包括interface/union)增加字段;這看似自找麻煩的機(jī)制實(shí)際上有很大用處,可以把高權(quán)限角色的特定字段使用extend寫(xiě)在另外的schema文件中,運(yùn)行時(shí)可合并解析,不同角色的用戶使用不同的schema。這樣可以通過(guò)加法來(lái)控制類型系統(tǒng)的可見(jiàn)性,避免內(nèi)省機(jī)制過(guò)度暴露類型系統(tǒng)。

DataLoader: 用于批量查詢,見(jiàn)后文介紹。

Relay: Facebook的另一個(gè)框架,應(yīng)該是基于GraphQL的,解決一些更高層的實(shí)際應(yīng)用問(wèn)題,比如通用的分頁(yè)機(jī)制等。

graphql-java特定術(shù)語(yǔ)

DataFetcher: 數(shù)據(jù)獲取器,即用以獲取Field實(shí)際值的對(duì)象。

Data Class: 數(shù)據(jù)類,這是graphql-java-tools中的概念,對(duì)應(yīng)schema中的同名對(duì)象類型。

可以在數(shù)據(jù)類上按照約定格式編寫(xiě)DataFetcher方法用于獲取簡(jiǎn)單字段值(比如無(wú)需另外查詢數(shù)據(jù)庫(kù)的字段)。

我在工程實(shí)踐中直接使用數(shù)據(jù)庫(kù)實(shí)體類作為數(shù)據(jù)類。

GraphQLResolver: 這是graphql-java-tools中的接口,帶有一個(gè)數(shù)據(jù)類的類型參數(shù)。

對(duì)該數(shù)據(jù)類定義部分或所有字段值的獲取方法,需要基于約定命名方法。

注意Resolver中的DataFetcher方法的優(yōu)先級(jí)高于DataClass中的方法。

我在工程實(shí)踐中直接使用Dao類作為對(duì)應(yīng)實(shí)體類的GraphQLResolver。

ExecutionInput: graphql-java中用來(lái)包裝一個(gè)完整查詢輸入的類,包括:

query - 查詢字符串;

operationName: 操作名; 可選;可用于在查詢中的多個(gè)操作中僅選擇特定名稱的予以執(zhí)行。

variables: 變量; 可選;一個(gè)Map,用于替換查詢字符串中形如"$value"的變量。

context - 上下文; 可選;任意Object類型,會(huì)被傳遞給DataFetcher;可用于傳遞當(dāng)前登錄用戶等。

root - 根對(duì)象; 可選;任意Object類型,會(huì)被傳遞給DataFetcher,語(yǔ)義上是被查詢的根對(duì)象。

ExecutionStrategy(執(zhí)行策略): 定義查詢的具體執(zhí)行策略。

比如是否異步執(zhí)行,多個(gè)子查詢是依次執(zhí)行,還是用線程池并發(fā)執(zhí)行等。

Instrumentation(攔截器): 比較像Servlet容器中的Filter,在查詢執(zhí)行前后各有一次執(zhí)行機(jī)會(huì)。

可用于對(duì)輸入和結(jié)果進(jìn)行額外處理;

支持鏈?zhǔn)綀?zhí)行;

需要指出的是DataLoader使用攔截器與核心系統(tǒng)耦合。

GraphqlFieldVisibility: 可以編程控制schema中各個(gè)字段的可見(jiàn)性。

和extend對(duì)應(yīng),相當(dāng)于用減法來(lái)控制類型系統(tǒng)的可見(jiàn)性。

技術(shù)選型

github上graphql-java名下的庫(kù)不少,如果希望了解各自簡(jiǎn)介的,可以看下awesome-graphql-java這個(gè)項(xiàng)目。
我自己評(píng)估了以下幾個(gè):

graphql-java: 這個(gè)是核心庫(kù),完全符合Facebook的spec,可以直接解析schema文件,但是類型綁定需要使用RuntimeWiring來(lái)編程方式添加,用起來(lái)還是比較麻煩的。

graphql-java-annotations: 這是數(shù)據(jù)驅(qū)動(dòng)的流派,使用注解直接在java類型上標(biāo)注GraphQL類型以及DataFetcher等,不用寫(xiě)schema文件。評(píng)估了一陣,個(gè)人感覺(jué)非常麻煩,比如:對(duì)每個(gè)字段都會(huì)創(chuàng)建新的DataFetcher實(shí)例來(lái)進(jìn)行解析,十分低效;要編寫(xiě)很多類來(lái)訪問(wèn)不同字段;過(guò)多的對(duì)象直接創(chuàng)建,難以托管到Spring容器;等等。因此我的結(jié)論是,此庫(kù)并不適用于我的工程實(shí)踐。

graphql-java-tools: 這是Schema驅(qū)動(dòng)的流派,這個(gè)庫(kù)使用Antlr自己重寫(xiě)了Schema解析器,使用GraphQLResolver實(shí)例和Data Class;基于方法名和參數(shù)的約定來(lái)定義DataFetching,使用起來(lái)很方便。這是我最終選定使用的庫(kù)。不太爽的地方有兩點(diǎn):1) 當(dāng)前版本基于graphql-java 7.0,遲滯于核心庫(kù) 2) 使用Kotlin編寫(xiě),我在MyEclipse里面無(wú)法正常設(shè)置斷點(diǎn)進(jìn)行跟蹤調(diào)試……

graphql-java-servlet: GraphQL不像傳統(tǒng)的REST,需要寫(xiě)一堆Controller,提供唯一的api接口即可,這個(gè)servlet就是幫你連這個(gè)都包辦的,不過(guò)我沒(méi)有用,自己基于SpringMVC寫(xiě)一個(gè)也很簡(jiǎn)單。

批量數(shù)據(jù)查詢(解決N+1問(wèn)題)

graphql-java提供了兩種批量數(shù)據(jù)查詢的方案:

BatchedDataFetcher: 用起來(lái)挺簡(jiǎn)單的,普通的DataFetcher是給你一個(gè)ID讓你返回一個(gè)對(duì)象,批量版是給你一個(gè)ID列表,讓你返回對(duì)應(yīng)的對(duì)象列表。不過(guò)這個(gè)不是Facebook推薦的方式,在新版本中會(huì)廢棄掉。

DataLoader: 這個(gè)是Facebook官方推薦的方式,nodejs中的實(shí)現(xiàn)是基于js的異步機(jī)制延遲查詢,把最近一個(gè)周期產(chǎn)生的多個(gè)查詢集中執(zhí)行(沒(méi)詳細(xì)了解,看文檔大概如此),java版實(shí)現(xiàn)方式則略有不同,下面詳細(xì)介紹。

關(guān)于DataLoader

graphql-java的dataloader是基于java8中新增的CompletableFeature類(大概相當(dāng)于javascript里面的Promise),實(shí)現(xiàn)異步延遲批量獲取查詢結(jié)果。

大概原理(個(gè)人理解):

在DataFetcher方法中,并不直接返回實(shí)體類T,而是調(diào)用DataLoader.load()方法,返回一個(gè)CompletionStage,這時(shí)并不立即進(jìn)行實(shí)際查詢,而是把這些異步階段對(duì)象緩存起來(lái)。

在查詢告一段落后(即能夠立即獲取的Field值都已取得,只剩下異步查詢未完成了),graphql-java會(huì)通過(guò)DataLoaderDispatcherInstrumentation.dispatch方法通知所有當(dāng)前注冊(cè)的DataLoader去執(zhí)行當(dāng)前積壓的所有異步階段對(duì)象,具體就是會(huì)使用DataLoader對(duì)應(yīng)的BatchLoader一次性查詢一批對(duì)象。

這時(shí)候又有一批Field的值已經(jīng)實(shí)際取得,繼續(xù)按查詢的請(qǐng)求向下層展開(kāi),如果有新的異步階段對(duì)象產(chǎn)生,就繼續(xù)步驟2,直到所有異步階段對(duì)象都獲得最終值。

工程實(shí)踐中對(duì)其應(yīng)用方式的考慮:
在graphql-java的官方教程中建議針對(duì)每請(qǐng)求創(chuàng)建新的DataLoader實(shí)例,查詢請(qǐng)求結(jié)束則DataLoader實(shí)例們的生命周期結(jié)束。
這個(gè)實(shí)現(xiàn)方式比較簡(jiǎn)單,不用考慮緩存的更新問(wèn)題,也不用考慮多個(gè)不同請(qǐng)求的緩存對(duì)象是否可共用。
舉個(gè)例子,張三和李四并發(fā)查詢張三的信息,他們獲取的"張三"用戶實(shí)例的結(jié)構(gòu)可能是不同的,這種情況這兩個(gè)并發(fā)請(qǐng)求就不能共用緩存,而應(yīng)該各自有獨(dú)立的DataLoader實(shí)例。
不過(guò)在我的工程實(shí)踐中,服務(wù)端內(nèi)存中的數(shù)據(jù)實(shí)體類都是客觀一致的,其結(jié)構(gòu)可見(jiàn)性應(yīng)在更上一層即DataFetcher甚至Schema級(jí)別中進(jìn)行過(guò)濾。
因此我的想法是為每種實(shí)體類維護(hù)單例的DataLoader,和Dao對(duì)象一一對(duì)應(yīng)。
這種情況下,就不能簡(jiǎn)單的使用DataLoader內(nèi)部默認(rèn)的簡(jiǎn)單內(nèi)存緩存了,因?yàn)榇司彺媸遣粫?huì)自動(dòng)定時(shí)清理的。
graphql-java是允許開(kāi)發(fā)者提供自己的緩存實(shí)現(xiàn)的,下一步我會(huì)結(jié)合項(xiàng)目中使用的Spring緩存管理器來(lái)具體實(shí)現(xiàn)。

查詢的緩存

graphql的查詢本身是有一定語(yǔ)法結(jié)構(gòu)的特殊文本,對(duì)該文本進(jìn)行解析也是有性能開(kāi)銷的,因此graphql-java提供了緩存機(jī)制方便開(kāi)發(fā)者把查詢文本的解析后數(shù)據(jù)結(jié)構(gòu)緩存起來(lái)。
以下代碼引自官方教程,我準(zhǔn)備結(jié)合我們項(xiàng)目里的EhCache來(lái)實(shí)作一下。

Cache cache = Caffeine.newBuilder().maximumSize(10_000).build();
GraphQL graphQL = GraphQL.newGraphQL(StarWarsSchema.starWarsSchema)
        .preparsedDocumentProvider(cache::get)
        .build();
關(guān)于訂閱的實(shí)現(xiàn)

工程實(shí)踐中使用WebSocket實(shí)現(xiàn)訂閱。
無(wú)論是graphql還是graphql-java都未指定訂閱的具體實(shí)現(xiàn)機(jī)制,但WebSocket是現(xiàn)代瀏覽器普遍支持的,高性能低限制的服務(wù)器推送機(jī)制。
SpringMVC支持WebSocket,同時(shí)支持在低版本瀏覽器中使用Sock.js作為兼容備選方案。
另外,graphql-java體驗(yàn)性支持的Defer數(shù)據(jù)獲取也可基于WebSocket實(shí)現(xiàn)。

未完待續(xù) 參考資料

基于spring和graphql-java-tools的寵物店例程
簡(jiǎn)單的TODO例程,使用relay的思路解決分頁(yè)問(wèn)題
基于WebSocket實(shí)現(xiàn)GraphQL訂閱

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69349.html

相關(guān)文章

  • 前端清單第 27 期:React Patent License 回復(fù),Shopify WebVR 購(gòu)

    摘要:新聞熱點(diǎn)國(guó)內(nèi)國(guó)外,前端最新動(dòng)態(tài)就開(kāi)源許可證風(fēng)波進(jìn)行回復(fù)數(shù)周前,基金會(huì)決定禁止旗下項(xiàng)目使用,因?yàn)槠湓跇?biāo)準(zhǔn)的許可證之外添加了專利聲明此舉引發(fā)了社區(qū)的廣泛討論,希望能夠更新其開(kāi)源許可證。 showImg(https://segmentfault.com/img/remote/1460000010777089); 前端每周清單第 27 期:React Patent License 回復(fù),Sho...

    jeffrey_up 評(píng)論0 收藏0
  • 前端每周清單第 10 期:Firefox53、React VR發(fā)布、Microsoft Edge現(xiàn)代

    摘要:新聞熱點(diǎn)國(guó)內(nèi)國(guó)外,前端最新動(dòng)態(tài)發(fā)布近日,正式發(fā)布新版本中提供了一系列的特性與問(wèn)題修復(fù)。而近日正式發(fā)布,其能夠幫助開(kāi)發(fā)者快速構(gòu)建應(yīng)用。 前端每周清單第 10 期:Firefox53、React VR發(fā)布、JS測(cè)試技術(shù)概述、Microsoft Edge現(xiàn)代DOM樹(shù)構(gòu)建及性能之道 為InfoQ中文站特供稿件,首發(fā)地址為這里;如需轉(zhuǎn)載,請(qǐng)與InfoQ中文站聯(lián)系。從屬于筆者的 Web 前端入門...

    MingjunYang 評(píng)論0 收藏0
  • 前端每周清單:Node.js 微服務(wù)實(shí)踐,Vue.js 與 GraphQL,Angular 組件技巧

    摘要:前端每周清單第期微服務(wù)實(shí)踐,與,組件技巧,攻防作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。 前端每周清單第 26 期:Node.js 微服務(wù)實(shí)踐,Vue.js 與 GraphQL,Angular 組件技巧,HeadlessChrome 攻防 作者:王下邀月熊 編輯:徐川...

    wall2flower 評(píng)論0 收藏0
  • 前端每周清單第 55 期: MobX 4 特性概覽,iOS Hacks 分享, 分布式事務(wù)詳解

    摘要:異步剪貼板操作過(guò)去的數(shù)年中,各瀏覽器基本上都在使用來(lái)進(jìn)行剪貼板交互。而提供了新的,則為我們提供了另一種異步式的剪貼板操作方式,本文即是對(duì)該機(jī)制與接口規(guī)范的詳細(xì)介紹。 showImg(https://segmentfault.com/img/remote/1460000013854167); 前端每周清單第 55 期: MobX 4 特性概覽,iOS Hacks 分享, 分布式事務(wù)詳解 ...

    zombieda 評(píng)論0 收藏0
  • 阿里云前端周刊 - 第 31 期

    摘要:發(fā)布按照官方發(fā)布計(jì)劃,的發(fā)布意味著進(jìn)入階段,徹底退出舞臺(tái),的還有半年結(jié)束。為了應(yīng)對(duì)這個(gè)挑戰(zhàn),美團(tuán)點(diǎn)評(píng)境外度假前端研發(fā)團(tuán)隊(duì)自年月起啟動(dòng)了面向端用戶的赫爾墨斯項(xiàng)目。前端技術(shù)越來(lái)越復(fù)雜,有不低的技術(shù)門檻。 推薦 1. 利用 Dawn 工程化工具實(shí)踐 MobX 數(shù)據(jù)流管理方案 https://zhuanlan.zhihu.com/p/... 項(xiàng)目在最初應(yīng)用 MobX 時(shí),對(duì)較為復(fù)雜的多人協(xié)作項(xiàng)...

    madthumb 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

MSchumi

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<