欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java GraphQL數(shù)據(jù)加載器批處理的實現(xiàn)詳解

 更新時間:2023年12月22日 15:26:42   作者:happyEnding  
GraphQL 數(shù)據(jù)加載器是優(yōu)化 GraphQL API 的關(guān)鍵組件,旨在解決臭名昭著的 N+1 查詢問題,在本中,我們將深入研究其批處理功能,感興趣的小伙伴可以了解下

介紹

GraphQL 是一種強大而靈活的 API 查詢語言,使客戶端能夠準確請求他們所需的數(shù)據(jù),從而消除信息的過度獲取和獲取不足。然而,隨著 GraphQL 查詢變得更加復雜并涉及多個數(shù)據(jù)源,有效地檢索數(shù)據(jù)并向客戶端提供數(shù)據(jù)可能具有挑戰(zhàn)性。這就是 GraphQL 數(shù)據(jù)加載器發(fā)揮作用的地方。

GraphQL 數(shù)據(jù)加載器是優(yōu)化 GraphQL API 的關(guān)鍵組件,旨在解決臭名昭著的 N+1 查詢問題,該問題在 GraphQL 服務(wù)器重復獲取相關(guān)項目列表的相同數(shù)據(jù)時發(fā)生。數(shù)據(jù)加載器通過批處理和緩存請求,幫助簡化從各種來源(例如數(shù)據(jù)庫、API,甚至本地緩存)獲取數(shù)據(jù)的過程。通過這樣做,他們顯著提高了 GraphQL 查詢的效率和性能。

在本中,我們將深入研究批處理功能,通過查看數(shù)據(jù)加載器的 java 實現(xiàn)來探索它如何發(fā)揮其魔力。

批處理

批處理是將多個單獨的數(shù)據(jù)檢索請求收集到單個批處理請求中的過程,從而減少對數(shù)據(jù)源的調(diào)用次數(shù)。在處理 GraphQL 查詢中的關(guān)系時,這一點尤其重要。

考慮一個典型場景,其中 GraphQL 查詢請求一個項目列表,以及每個項目的附加相關(guān)數(shù)據(jù)(例如用戶信息)。如果不進行批處理,這將導致對每個項目進行單獨的數(shù)據(jù)庫查詢或 API 請求,從而導致 N+1 查詢問題。通過批處理,可以將這些單獨的請求有效地組合成單個請求,從而大大減少數(shù)據(jù)源的往返次數(shù)

Java 數(shù)據(jù)加載器批處理

假設(shè)我們有一個如下所示的 graphql 查詢

{
  user {
    name
    friends {
      name
    }
  }
}

它生成以下查詢結(jié)果

{
  "user": {
    "name": "zhangsan",
    “friends”: [
      {
        "name": "lisi",
      },
      {
        "name": "wanmgwu",
      },
      {
        "name": "zhouliu",
      }
    ]
  }
}

一個簡單的實現(xiàn)方法是為查詢響應中的每個用戶執(zhí)行一次調(diào)用以檢索一個用戶對象,即 4 次調(diào)用,一次針對根對象,一次針對列表中的每個好友。

然而,它DataLoader不會立即執(zhí)行遠程調(diào)用,它只是將調(diào)用排入隊列并返回一個 Promise ( CompletableFuture) 來傳遞用戶對象。一旦我們將構(gòu)建查詢結(jié)果的所有調(diào)用排入隊列,我們??必須請求DataLoader開始執(zhí)行它們。這就是奇跡發(fā)生的地方。將DataLoader開始提取每次調(diào)用的用戶 ID 并將其放入一個列表中,該列表將用于查詢我們配置的后端,并僅使用一個請求即可檢索用戶列表。

批處理通常按級別進行,在本例中我們有 2 個級別。root 用戶和他的朋友。通過使用DataLoaderbatchig,此響應將只需要 2 次調(diào)用。

代碼示例

讓我們添加一些代碼來展示如何使用它。

我們首先需要擁有一個BatchLoader. 它將從用戶后端批量加載用戶,從而減少對該后端的 API 調(diào)用量。

List<User> loadUsersById(List<Long> userIds) {
   System.out.println("Api call to load users = " + userIds);
   return users.stream().filter(u -> userIds.contains(u.id())).toList();
}

BatchLoader<Long, User> userBatchLoader = new BatchLoader<>() {
   @Override
   public CompletionStage<List<User>> load(List<Long> userIds) {
      return CompletableFuture.supplyAsync(() -> {
         return loadUsersById(userIds);
      });
   }
};

然后我們需要創(chuàng)建一個DataLoader將使用前面的BatchLoader來執(zhí)行整個用戶樹的加載。

var userLoader = DataLoaderFactory.newDataLoader(userBatchLoader);

var userDTO = new UserDTO();
userLoader.load(1L).thenAccept(user -> {
   userDTO.id = user.id();
   userDTO.name = user.name();
   user.friends().forEach(friendId -> {
      userLoader.load(friendId).thenAccept(friend -> {
         userDTO.friends.add(new FriendDTO(friend.id(), friend.name()));
      });
   });
});

userLoader.dispatchAndJoin();
System.out.println(userDTO);

它將產(chǎn)生以下調(diào)試輸出

Api call to load users = [1]
Api call to load users = [2, 3, 4]
UserDTO{id=1, name='John', friends=[FriendDTO[id=2, name=Jane], FriendDTO[id=3, name=Bob], FriendDTO[id=4, name=Alice]]}

如果您對它的內(nèi)部工作原理感到好奇,我將向您展示用戶的一種自定義實現(xiàn)DataLoader。不是真正的。只需一個簡化版本即可幫助您了解全貌。

static class UserLoader {
   BatchLoader<Long, User> userBatchLoader;

   record QueueEntry(long id, CompletableFuture<User> value) { }
   List<QueueEntry> loaderQueue = new ArrayList<>();

   UserLoader(BatchLoader<Long, User> userBatchLoader) {
      this.userBatchLoader = userBatchLoader;
   }

   CompletableFuture<User> load(long userId) {
      var future = new CompletableFuture<User>();
      loaderQueue.add(new QueueEntry(userId, future));
      return future;
   }

   List<User> dispatchAndJoin() {
      List<User> joinedResults = dispatch().join();
      List<User> results = new ArrayList<>(joinedResults);
      while (loaderQueue.size() > 0) {
         joinedResults = dispatch().join();
         results.addAll(joinedResults);
      }
      return results;
   }

   CompletableFuture<List<User>> dispatch() {
      var userIds = new ArrayList<Long>();
      final List<CompletableFuture<User>> queuedFutures = new ArrayList<>();

      loaderQueue.forEach(qe -> {
         userIds.add(qe.id());
         queuedFutures.add(qe.value());
      });

      loaderQueue.clear();

      var userFutures = userBatchLoader.load(userIds).toCompletableFuture();

      return userFutures.thenApply(users -> {
         for (int i = 0; i < queuedFutures.size(); i++) {
            var userId = userIds.get(i);
            var user = users.get(i);
            var future = queuedFutures.get(i);
            future.complete(user);
         }
         return users;
      });
   }
}

所以,首先看一下CompletableFuture<User> load(long userId),它不執(zhí)行任何 userId 查找,它只是:

  • 將查找排入隊列
  • 生成一個,CompletableFuture讓您根據(jù)您提供的查找鏈接進一步查找。因此,查找被推遲,直到我們實際使用dispatchAndJoin()

現(xiàn)在,看看List<User> dispatchAndJoin()。一旦我們準備好檢索用戶列表,就會調(diào)用該函數(shù)。它會:

1.調(diào)用 CompletableFuture<List<User>> dispatch()將執(zhí)行以下操作:

將所有 userId 分組到一個列表中,并將其發(fā)送到底層BatchLoader ,底層對后端執(zhí)行實際的 API 調(diào)用。

完成我們注冊查找時(當我們調(diào)用 )時提供的 CompletableFuture CompletableFuture<User> load(long userId),從而向 中添加更多元素loaderQueue。此時,下一級的 userId 查找已排隊。

2.當中還有剩余元素時重復該過程loaderQueue。

到此這篇關(guān)于Java GraphQL數(shù)據(jù)加載器批處理的實現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Java GraphQL數(shù)據(jù)加載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis初始化知識小結(jié)

    Mybatis初始化知識小結(jié)

    Mybatis的初始化過程就是加載自己運行時所需要的配置信息的過程,這篇文章主要介紹了Mybatis初始化知識小結(jié),需要的朋友可以參考下
    2021-10-10
  • java?11新特性HttpClient主要組件及發(fā)送請求示例詳解

    java?11新特性HttpClient主要組件及發(fā)送請求示例詳解

    這篇文章主要為大家介紹了java?11新特性HttpClient主要組件及發(fā)送請求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 關(guān)于Spring?Cloud的熔斷器監(jiān)控問題

    關(guān)于Spring?Cloud的熔斷器監(jiān)控問題

    Turbine是一個聚合Hystrix監(jiān)控數(shù)據(jù)的工具,它可將所有相關(guān)/hystrix.stream端點的數(shù)據(jù)聚合到一個組合的/turbine.stream中,從而讓集群的監(jiān)控更加方便,接下來通過本文給大家介紹Spring?Cloud的熔斷器監(jiān)控,感興趣的朋友一起看看吧
    2022-01-01
  • HashSet工作原理_動力節(jié)點Java學院整理

    HashSet工作原理_動力節(jié)點Java學院整理

    HashSet 底層采用 HashMap 來保存所有元素,因此 HashSet 的實現(xiàn)比較簡單。接下來通過本文給大家介紹HashSet工作原理_動力節(jié)點Java學院整理,需要的朋友可以參考下
    2017-04-04
  • java設(shè)計模式筆記之適配器模式

    java設(shè)計模式筆記之適配器模式

    這篇文章主要為大家詳細介紹了java設(shè)計模式之適配器模式筆記,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Spring Boot 端口被占用的解決方法

    Spring Boot 端口被占用的解決方法

    這篇文章主要介紹了解決 Spring Boot 端口被占用的方法詳解,通過本文的介紹,你學習了如何解決 Spring Boot 端口被占用的問題。你了解了檢查端口是否被占用、停止占用端口的進程、更改應用程序的端口號以及檢查應用程序間的端口沖突等方法,需要的朋友可以參考下
    2023-07-07
  • Java異常處理Guava?Throwables類使用實例解析

    Java異常處理Guava?Throwables類使用實例解析

    這篇文章主要為大家介紹了Java異常處理神器Guava?Throwables類使用深入詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Spring Boot 指定外部啟動配置文件詳解

    Spring Boot 指定外部啟動配置文件詳解

    在springboot項目中,也可以使用yml類型的配置文件代替properties文件。接下來通過本文給大家分享Springboot配置文件的使用,感興趣的朋友一起看看吧
    2021-09-09
  • Java之InputStreamReader類的實現(xiàn)

    Java之InputStreamReader類的實現(xiàn)

    這篇文章主要介紹了Java之InputStreamReader類的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • Nacos配合SpringBoot實現(xiàn)動態(tài)線程池的基本步驟

    Nacos配合SpringBoot實現(xiàn)動態(tài)線程池的基本步驟

    使用Nacos配合Spring Boot實現(xiàn)動態(tài)線程池,可以讓你的應用動態(tài)地調(diào)整線程池參數(shù)而無需重啟,這對于需要高度可配置且需要適應不同負載情況的應用來說非常有用,本文給大家介紹實現(xiàn)動態(tài)線程池的基本步驟,需要的朋友可以參考下
    2024-02-02

最新評論