graphql-java使用手冊:part6 使用 Dataloader

原文:http://blog.mygraphql.com/wordpress/?p=110

使用 Dataloader

使用 graphql, 你很可能會去查詢圖結構的數(shù)據(jù)(graph of data )
(這可能是句廢話).
如果用簡單直接的方法去獲取每個field的數(shù)據(jù),可能會效率很低。

使用 java-dataloader 可以幫助你更有效地緩存和批量化數(shù)據(jù)加載操作。
>><<dataloader會緩存所有加載過的數(shù)據(jù),使再次使用相同數(shù)據(jù)時,不需要再加載。

假設我們用以下的 StarWars 查詢。這查詢了一個英雄(
hero)和他朋友的名字,和他朋友的朋友的名字。很多時候,他們有共同的朋友。

{
    hero {
        name
        friends {
            name
            friends {
               name
            }
        }
    }
}

下面是查詢的結果。你可以看到,Han, Leia, Luke 和 R2-D2
是一群緊密的朋友。他們有很多共同的朋友。

[hero: [name: 'R2-D2', friends: [
        [name: 'Luke Skywalker', friends: [
                [name: 'Han Solo'], [name: 'Leia Organa'], [name: 'C-3PO'], [name: 'R2-D2']]],
        [name: 'Han Solo', friends: [
                [name: 'Luke Skywalker'], [name: 'Leia Organa'], [name: 'R2-D2']]],
        [name: 'Leia Organa', friends: [
                [name: 'Luke Skywalker'], [name: 'Han Solo'], [name: 'C-3PO'], [name: 'R2-D2']]]]]
]

一個直接的實現(xiàn)是為每個人物對象(person object)調(diào)用一次 DataFetcher
去獲取數(shù)據(jù)。

這樣你需要 15 次網(wǎng)絡調(diào)用 。即使這群有有很多相同的朋友。使用
dataloader 可以讓 graphql 查詢更高效。

graphql 會批量化每個層級的查詢。 ( 如先是 hero 之后是 friends
之后是他們的 friends), Data loader 返回了一個 ”
期約(promise)”,期約會返回一個 person
object.(人物對象)。在查詢的每個層級, dataloader.dispatch()
方法均會被調(diào)用一次,以獲取真實的數(shù)據(jù)。當開啟了緩存功能時
(默認開啟),將直接返回之前加載過的 person,而不會再發(fā)起一次查詢。

上例中,共有 5 個獨立的 people。但當緩存和批量化開啟后,只發(fā)起了 3
次調(diào)用 batch loader 方法的查詢操作。*3* 次網(wǎng)絡或DB訪問,當然比 15
次牛B多了。【譯者補】

如果你使用了如 java.util.concurrent.CompletableFuture.supplyAsync()
的異步程序方式 。多個field的遠程加載數(shù)據(jù)就可以并發(fā)進行了。.
這可以讓查詢更快,因一次并發(fā)了多個遠程調(diào)用。

下面就是示例代碼:

// a batch loader function that will be called with N or more keys for batch loading
BatchLoader<String, Object> characterBatchLoader = new BatchLoader<String, Object>() {
    @Override
    public CompletionStage<List<Object>> load(List<String> keys) {
        //
        // we use supplyAsync() of values here for maximum parellisation
        //
        return CompletableFuture.supplyAsync(() -> getCharacterDataViaBatchHTTPApi(keys));
    }
};

// a data loader for characters that points to the character batch loader
DataLoader<String, Object> characterDataLoader = new DataLoader<>(characterBatchLoader);

//
// use this data loader in the data fetchers associated with characters and put them into
// the graphql schema (not shown)
//
DataFetcher heroDataFetcher = new DataFetcher() {
    @Override
    public Object get(DataFetchingEnvironment environment) {
        return characterDataLoader.load("2001"); // R2D2
    }
};

DataFetcher friendsDataFetcher = new DataFetcher() {
    @Override
    public Object get(DataFetchingEnvironment environment) {
        StarWarsCharacter starWarsCharacter = environment.getSource();
        List<String> friendIds = starWarsCharacter.getFriendIds();
        return characterDataLoader.loadMany(friendIds);
    }
};

//
// DataLoaderRegistry is a place to register all data loaders in that needs to be dispatched together
// in this case there is 1 but you can have many
//
DataLoaderRegistry registry = new DataLoaderRegistry();
registry.register("character", characterDataLoader);

//
// this instrumentation implementation will dispatch all the dataloaders
// as each level fo the graphql query is executed and hence make batched objects
// available to the query and the associated DataFetchers
//
DataLoaderDispatcherInstrumentation dispatcherInstrumentation
        = new DataLoaderDispatcherInstrumentation(registry);

//
// now build your graphql object and execute queries on it.
// the data loader will be invoked via the data fetchers on the
// schema fields
//
GraphQL graphQL = GraphQL.newGraphQL(buildSchema())
        .instrumentation(dispatcherInstrumentation)
        .build();

需要注意的是,只有你使用了 DataLoaderDispatcherInstrumentation
,上面說的才會生效。由它來調(diào)用 dataLoader.dispatch() 。不然,期約(
promises ) 將不會被執(zhí)行,就更不會有數(shù)據(jù)獲取了。

查詢范圍的 Data Loaders

對于 Web
請求,請求的結果可能會因不同用戶而不同的。如果是特定用戶的數(shù)據(jù),你一定不希望用戶A的數(shù)據(jù),被用戶B查詢到。

所以 DataLoader 實例的范圍很重要。這時,你需要對每個 Request
創(chuàng)建一個新的 DataLoader,來保證它只在當前請求中生效。

如果你需要的是不同請求間共享數(shù)據(jù),所以你會希望 DataLoader
的生命周期更長。

但如用你用請求級的 data loaders ,為每個請求創(chuàng)建 GraphQL and
DataLoader 是花費很少資源的。Its the GraphQLSchema creation that can
be expensive, especially if you are using graphql SDL parsing.

i在代碼中靜態(tài)引用 schema ??梢允庆o態(tài)變量或 IoC
單件組件。但每次處理請求時,都需要創(chuàng)建 GraphQL 對象。

GraphQLSchema staticSchema = staticSchema_Or_MayBeFrom_IoC_Injection();

DataLoaderRegistry registry = new DataLoaderRegistry();
registry.register("character", getCharacterDataLoader());

DataLoaderDispatcherInstrumentation dispatcherInstrumentation
        = new DataLoaderDispatcherInstrumentation(registry);

GraphQL graphQL = GraphQL.newGraphQL(staticSchema)
        .instrumentation(dispatcherInstrumentation)
        .build();

graphQL.execute("{ helloworld }");

// you can now throw away the GraphQL and hence DataLoaderDispatcherInstrumentation
// and DataLoaderRegistry objects since they are really cheap to build per request
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,790評論 25 709
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,075評論 4 61
  • 一.相信有不少小伙伴還在為Apk上架應用市場而苦惱吧,現(xiàn)在就分享一下apk從代碼變?yōu)榭缮蟼鲬檬袌龅陌惭b包的詳細流...
    MiHomes閱讀 7,085評論 17 37
  • 什么是語言?我們眼里看的、嘴里說的、手里寫的、耳朵聽的,所有所有的話都叫做語言。首先,毫無疑問,語言是交流的...
    打個背包去旅行閱讀 970評論 0 4

友情鏈接更多精彩內(nèi)容