GraphQL(三)DataLoader 详解

news2024/11/28 18:58:51

DataLoader是一个通用实用程序,用作应用程序数据获取层的一部分,通过批处理缓存为各种远程数据源(如数据库或 Web 服务)提供简化且一致的 API

GraphQl backend

批处理

const user = await userLoader.load(1);
const invitedBy = await userLoader.load(user.invitedByID);
console.log(`User 1 was invited by ${invitedBy}`);

// Elsewhere in your application
const user = await userLoader.load(2);
const lastInvited = await userLoader.load(user.lastInvitedID);
console.log(`User 2 last invited ${lastInvited}`);

一个简单的应用程序可能已经发出四次到后端的往返请求以获取所需的信息,但是使用DataLoader这个应用程序最多只会进行两次请求

DataLoader

DataLoader 允许您在不牺牲批量数据加载性能的情况下解耦应用程序的不相关部分。虽然加载器提供了一个加载单个值的 API,但所有并发请求将被合并至批量加载函数来使用。这允许您的应用程序在整个应用程序中安全地分发数据获取要求,并保持最少的传出数据请求

批量调度

在同一GraphQL请求中调用DataLoader多次时,DataLoader将收集这些调用,并在GraphQL请求结束时一次性加载所有数据。这可以减少网络延迟和数据库负载,从而提高数据加载的速度

在graphql-java中,可以修改在DataLoader构造函数中BatchingOptions对象的maxBatchSizebatchingEnabled属性

  • maxBatchSize:设置DataLoader在执行批处理之前等待负载项的最大数量。默认值为Integer.MAX_VALUE,这意味着DataLoader将收集所有负载项并一次性加载它们。
  • batchingEnabled:启用或禁用DataLoader的批处理功能。如果将其设置为false,则DataLoader将在每个负载项上立即执行加载操作,而不是等待负载项集满
BatchingOptions batchingOptions = BatchingOptions.newOptions().setMaxBatchSize(100).setBatchingEnabled(true).build();
DataLoader<Key, Value> dataLoader = new DataLoader<>(batchLoader, batchingOptions);

缓存

DataLoader 为应用程序的单个请求中发生的所有加载提供记忆缓存

当使用给定键调用一次load()函数后,结果值将被缓存以避免重复加载。更准确地说load()是一个记忆函数

DataLoader 缓存不会取代 Redis、Memcache 或任何其他共享应用程序级缓存。DataLoader首先是一种数据加载机制,它的缓存只是为了不在你的Application的单次请求上下文中重复加载相同的数据。为此,它维护了一个内存中记忆缓存

更多关于Dataloader缓存相关知识,如清除缓存、缓存错误等见DataLoader Caching

graphql-java

java-dataloader是参考Facebook指引实现的一个功能完整的应用,但在人工调度功能上有一个重要的区别。

java-dataloader的特点是:

  • 简单、直观的 API,使用泛型和流畅的编码
  • 使用 lambda 表达式定义批量加载函数
  • 在队列中安排加载请求以进行批处理
  • 从代码中的任何位置添加加载请求
  • 请求返回一个CompleteableFuture请求值
  • 可以一次创建多个请求
  • 缓存加载请求,因此只获取一次数据
  • 可以清除单个缓存键,以便在下一批队列调度时重新获取数据
  • 可以使用键/值填充缓存,以避免不必要地获取数据
  • 可以使用 lambda 表达式配置缓存键函数,以从复杂的数据加载器键类型中提取缓存键
  • 随着批处理的进行,单个批次期货完成/解决
  • 结果根据加载请求的插入顺序排序
  • 当批处理 future 失败时处理部分错误
  • 可以在配置中禁用批处理和/或缓存
  • 可以提供您自己的CacheMap<K, V>实现
  • 可以提供您自己的ValueCache<K, V>实现
  • 具有非常高的测试覆盖率

BatchLoader

一个DataLoader对象需要一个BatchLoader函数,该函数负责在给定键列表的情况下加载值

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

DataLoader<Long, User> userLoader = DataLoaderFactory.newDataLoader(userBatchLoader);

DataLoader主要利用了Java的CompletableFuture异步任务收集再批量处理,最后将结果写回对应任务,故使用CompleteableFuture加载数据

CompletableFuture<User> load1 = userLoader.load(1L);

在GraphQL查询的每个层级,dataloader.dispatch()将被自动调用以触发该部分查询的批处理请求

缓存

DataLoader有一个两层缓存系统:

  1. 第一个缓存由org.dataloader.CacheMap接口表示。它将CompletableFuture按key缓存,因此在之后load(key)加载同一个key,会返回相同的Future

该缓存只能在 JVM 本地工作,因为它的缓存CompletableFuture无法通过网络序列化。

  1. 二级缓存是以org.dataloader.ValueCache接口表示。默认情况下,这缓存未启用并且是空操作。

值缓存使用异步 API 模式来封装值缓存,以支持外部缓存实现(例如 REDIS 或 Memcached)的想法。

Statistics

java-dataloader的org.dataloader.stats.Statistics用于收集DataLoader执行过程中的状态,比如加载次数、缓存命中次数等

默认情况下是不会执行数据收集的,需要通过DataLoaderDispatcherInstrumentation进行注入

@Bean
public DataLoaderDispatcherInstrumentation dataLoaderDispatcherInstrumentation(){
    return new DataLoaderDispatcherInstrumentation(DataLoaderDispatcherInstrumentationOptions.newOptions().includeStatistics(true));
}

Dataloader实例

Client

@Component
public class PointClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(PointClient.class);

    /**
     * 按月查询分值
     *
     * @param uidList   uid列表
     * @param month yyyyMM
     * @return
     */
    public Mono<Map<Long, Integer>> batchGetMonthPoint(Collection<Long> uidList, String month) {
        return builder.build().post().uri(BATCH_GET_MONTH_POINT_URI).bodyValue(new BatchUidMonthPointReq(uidList, month))
                .retrieve().bodyToMono(BatchUidMonthPointRes.class).retry(RETRY_TIME).map(rsp -> {
                    LOGGER.info("batchGetMonthPoint uidList:{} rsp:{}", uidList, rsp);
                    if (rsp.isSuccess()) {
                        return rsp.getPointMonthList().stream().collect(Collectors.toMap(
                                p -> Long.parseLong(p.getUid()), p -> p.getPoint().intValue()));
                    }
                    return Collections.emptyMap();
                });
    }
}

DataLoader

@DgsDataLoader(name = "pointLoader")
public class PointBatchLoader implements MappedBatchLoaderWithContext<Long, Integer> {
    @Autowired
    private PointClient pointClient;

    @Override
    public CompletionStage<Map<Long, Integer>> load(Set<Long> uidList, BatchLoaderEnvironment batchLoaderEnvironment) {
        return pointClient.batchGetMonthPoint(uidList, LocalDate.now().format("yyyyMM")).toFuture();
    }
}

DgsData

@DgsData(parentType = "User")
public CompletableFuture<Integer> point(DgsDataFetchingEnvironment dfe){
    User user = dfe.getSource();
    DataLoader<Long, Integer> dataLoader = dfe.getDataLoader(PointBatchLoader.class);
    return dataLoader.load(user.getUid());
}

源码

Dataloader加载及缓存机制可见org.dataloader.DataLoaderHelper

load

若启动缓存,优先从缓存中获取Future。若启动批处理,加入loader队列,待自动执行;否则立即执行该loader

CompletableFuture<V> load(K key, Object loadContext) {
    synchronized (dataLoader) {
        boolean batchingEnabled = loaderOptions.batchingEnabled();
        boolean cachingEnabled = loaderOptions.cachingEnabled();

        stats.incrementLoadCount(new IncrementLoadCountStatisticsContext<>(key, loadContext));

        if (cachingEnabled) {
            return loadFromCache(key, loadContext, batchingEnabled);
        } else {
            return queueOrInvokeLoader(key, loadContext, batchingEnabled, false);
        }
    }
}

dispatch

启动批处理时才会执行的loader分发,达到最大批量配置则执行loader加载。可由ScheduledDataLoaderRegistry定时触发或dispatchAndJoin手动触发

DispatchResult<V> dispatch() {
    boolean batchingEnabled = loaderOptions.batchingEnabled();
    //
    // we copy the pre-loaded set of futures ready for dispatch
    final List<K> keys = new ArrayList<>();
    final List<Object> callContexts = new ArrayList<>();
    final List<CompletableFuture<V>> queuedFutures = new ArrayList<>();
    synchronized (dataLoader) {
        loaderQueue.forEach(entry -> {
            keys.add(entry.getKey());
            queuedFutures.add(entry.getValue());
            callContexts.add(entry.getCallContext());
        });
        loaderQueue.clear();
        lastDispatchTime.set(now());
    }
    if (!batchingEnabled || keys.isEmpty()) {
        return new DispatchResult<>(completedFuture(emptyList()), 0);
    }
    final int totalEntriesHandled = keys.size();

    int maxBatchSize = loaderOptions.maxBatchSize();
    CompletableFuture<List<V>> futureList;
    if (maxBatchSize > 0 && maxBatchSize < keys.size()) {
        futureList = sliceIntoBatchesOfBatches(keys, queuedFutures, callContexts, maxBatchSize);
    } else {
        futureList = dispatchQueueBatch(keys, callContexts, queuedFutures);
    }
    return new DispatchResult<>(futureList, totalEntriesHandled);
}

参考资料:

  1. dataloader
  2. 使用数据加载器
  3. java-dataloader

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】7. auto和nullptr(c++11)

文章目录 一、auto二、nullptr 一、auto 在C98中&#xff0c;auto是一个存储类说明符&#xff0c;表示变量具有自动存储期&#xff0c;即在函数或块的作用域内创建和销毁。 在C11中&#xff0c;auto是一个类型占位符&#xff0c;表示变量的类型由其初始化器自动推断。 使用如下…

分类和扩展与继承

文章目录 [TOC](文章目录) 分类定义分类的使用使用场景使用注意点 Extension 扩展分类和扩展的区别 继承的定义使用注意点 新建一个分类 分类基础知识 分类 分类是指为已有的类添加方法&#xff0c;也可以说是将很多很复杂的代码划分为几个分区。 定义 分类的作用是扩展已有…

第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组题解

试题 A: 阶乘求和 本题总分&#xff1a;5 分 【问题描述】 令 S 1! 2! 3! ... 202320232023!&#xff0c;求 S 的末尾 9 位数字。 提示&#xff1a;答案首位不为 0。 【答案提交】 这是一道结果填空的题&#xff0c;你只需要算出结果后提交即可。本题的结果为一 个整数&am…

Linux信号:SIGCHLD信号和僵尸进程

1. SIGCHLD信号产生条件&#xff1a; &#xff08;1&#xff09;子进程终止&#xff1b; &#xff08;2&#xff09;子进程收到SIGSTOP暂停&#xff1b; &#xff08;3&#xff09;子进程处于暂停状态&#xff0c;收到SIGCONT被唤醒。 2. 捕捉SIGCHLD&#xff0c;避免僵尸进程&…

网络钓鱼:工作场所保护电子邮件安全的五个步骤

导语&#xff1a;Sophos电子邮件产品管理高级总监David Mitchell分享了他的主要技巧&#xff0c;以优化工作场所的电子邮件安全性。 Sophos电子邮件产品管理高级总监David Mitchell分享了他的主要技巧&#xff0c;以优化工作场所的电子邮件安全性。 尽管工作场所的聊天和即时…

云原生时代崛起的编程语言Go基础实战

文章目录 概述定义使用场景Go 安全 使用须知搜索工具Go基础命令标准库 基础语法Effective Go 概览命名规范注释变量常量(const)控制结构数据类型迭代&#xff08;range&#xff09;函数指针字符串和符文结构体(struct)方法接口(interface)泛型错误&#xff08;errors&#xff0…

iOS描述文件(.mobileprovision)一键申请

转载&#xff1a;IOS描述文件制作教程 iOS描述文件(.mobileprovision)一键申请 在主界面上点击描述文件按钮。 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 新建ios描述文件 然后点击新建&#xff0c;然后输入描述文件名称&…

数据库设计与前端框架

数据库设计与前端框架 学习目标&#xff1a; 理解多租户的数据库设计方案 熟练使用PowerDesigner构建数据库模型理解前端工程的基本架构和执行流程 完成前端工程企业模块开发 多租户SaaS平台的数据库方案 多租户是什么 多租户技术&#xff08;Multi-TenancyTechnology&a…

【密码算法 之六】CCM 浅析

CCM模式浅析 1. 综述2. 加密2.1 前置条件&#xff08;Prerequisites&#xff09;2.2 输入&#xff08;Input&#xff09;2.3 输出&#xff08;Output&#xff09;2.4 加密流程&#xff08;Steps&#xff09; 3. 解密3.1 前置条件&#xff08;Prerequisites&#xff09;3.2 输入…

Orangepi Zero2 全志H616简介

为什么学 学习目标依然是Linux 系统 &#xff0c;平台是 ARM 架构 蜂巢快递柜&#xff0c;配送机器人&#xff0c;这些应用场景用C51,STM32单片机无法实现 第三方介入库的局限性&#xff0c;比如刷脸支付和公交车收费设备需要集成支付宝SDK&#xff0c;提供的libalipay.so 是…

【VM服务管家】VM4.0平台SDK_2.2 模块API类

目录 2.2.1 方案保存&#xff1a;方案高速保存的方法2.2.2 Group模块&#xff1a;Group输入输出图像数据的方法2.2.3 模块操作类&#xff1a;设置输入图像、参数和ROI的方法2.2.4 图像源&#xff1a;通过图像源模块接口设置图像输入的方法2.2.5 图像源&#xff1a;通过SDK传入相…

go 语言环境安装(Windows 系统下安装)

go 语言官网:The Go Programming Language 下载 go 安装包的网址&#xff1a;All releases - The Go Programming Language go 支持很多种操作系统 Windows 系统下 - 安装和配置SDK 一、SDK 介绍 SDK 的全称是 Software Development Kit &#xff0c;即 软件开发工具包 二…

STM32物联网实战开发(1)——全新的程序框架

现在STM32公司主推的是HAL库的开发&#xff0c;标准库已经不再更新。通过STM32cubeMX的图形界面生成代码非常的方便。 一、程序框架的构想 1、STM32cubeMX 生成的代码与添加的应用代码分离&#xff1b; 2、利用 STM32cubeMX 重新生成代码&#xff0c;不影响应用代码&#xf…

zabbix配置钉钉告警(附含钉钉告警脚本 · 实战亲测无任何问题)

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录 钉钉上操作&#xff08;钉钉告警以关…

密码学新进展:基于同态加密的数据保护是否会成为未来的趋势?

第一章&#xff1a;引言 在今天这个数字时代&#xff0c;数据的重要性不断增加&#xff0c;已经成为了现代社会最宝贵的财富之一。各种机构和企业在日常运营中都会处理大量的数据&#xff0c;包括用户信息、财务数据、产品销售数据等。这些数据的安全性非常重要&#xff0c;因…

2.5 定点运算器的组成

学习目标&#xff1a; 具体包括以下几点&#xff1a; 了解定点运算器的基本概念和分类&#xff0c;包括定点运算器的分类、常见的定点运算器类型等&#xff1b;学习定点运算器的基本组成部分&#xff0c;包括输入/输出接口、寄存器、算术逻辑单元(ALU)、控制单元等&#xff0…

VS Code C++ 输出窗口中文乱码问题解决

VS Code C 输出窗口中文乱码问题解决 系统cmd终端乱码 的情况&#xff1a;原因解决方法&#xff1a;&#xff08;仅针对cmd终端输出的情况&#xff09;方法一&#xff1a;更改代码文件的编码方法二 &#xff1a;更改cmd默认终端的编码方式 系统cmd终端乱码 的情况&#xff1a; …

2023年的深度学习入门指南(10) = 前端同学如何进行chatgpt开发

2023年的深度学习入门指南(10) 前端同学如何进行chatgpt开发 在第二篇&#xff0c;我们使用openai的python库封装&#xff0c;搞得它有点像之前学习的PyTorch一样的库。这一节我们专门给它正下名&#xff0c;前端就是字面意义上的前端。 给gpt4写前端 下面我们写一个最土的…

“BIM+智慧工地”精准“数字化”变身智慧工程“管家”

用手机对着满载钢筋的卡车拍照&#xff0c;手指选定一下钢筋范围&#xff0c;几秒后&#xff0c;屏幕就能迅速识别车上有多少根钢筋——这是建筑产业数字化管理智慧工程的应用领域之一。 投资1.78亿元建设的贵州民航产教融合实训基地是集实践教学、社会培训、企业生产保障和科研…

学历与就业:我对“孔乙已长衫”现象的思考

一、你认为社会对于学历和职业之间的关系认知是怎样的&#xff1f; 在当前的社会中&#xff0c;学历往往被看作是一个人能否获得好工作的重要标准。许多用人单位更愿意录取拥有更高学历的求职者&#xff0c;因为他们通常具备更广阔的知识视野和更强的理论基础。然而&#xff0…