概述
在本课中,我们将
- 讨论什么是数据加载器
- 介绍
dataloader
包 - 回顾数据加载器的要求
- 详细了解数据加载器在底层的运作方式
数据加载器
为了解决我们应用程序中的 n+1 问题,我们将使用 数据加载器。
数据加载器的主要工作是将多个相似的请求替换为一个批处理请求。在我们的示例中,我们看到了三个几乎完全相同的请求,它们使用特定的展示 ID 来返回 tiện nghi 数据。使用数据加载器,这会变成一个 单个 请求,一次获取所有三个展示的数据。
我们将会向我们的数据源 类中引入数据加载器的强大功能,并使用 GraphQL dataloader
包。
dataloader
包
dataloader
包是一个实用程序,它提供批处理和缓存功能。这让我们 将 各种键批处理在一起,使用一个请求,一次性提供所有数据!
让我们用以下 查询 来对此进行说明。
query GetFeaturedListingsAmenities {featuredListings {idtitleamenities {idnamecategory}}}
对于查询解析出的每个精选列表对象,将调用 查询。当前,此解析器使用列表 ID 来独立解析 Listing.amenities
解析器。每个请求。(导致对同一端点的三个单独的网络请求!)
我们想要的行为截然不同:我们希望数据加载器收集 所有参与 查询(也称为 密钥)的列表 ID,并执行一个请求。
在我们的示例中,这意味着在使用每个列表的 ID 调用 Listing.amenities
解析器时,它不会直接调用命中我们 REST API 的 ListingAPI
的方法;而会将每个密钥传递给一个数据加载器方法,它可以将整个 操作所需的密钥批处理在一起。
在将所有列表 ID 收集到一个列表中后,数据加载器可以承担从 数据源实际请求数据的责任。它可以一次向 REST API 端点分派一个请求,用于所有 ID同时——与让 解析器为 每个发起网络请求相比,性能大幅提升!
最棒的是,我们的数据加载器方法会自动对我们传递给它们的标识符进行重复数据删除。这意味着,如果我们的 查询包含多个具有相同 ID 的列表,我们只会请求一次列表的便利设施。
数据加载器需要什么
数据加载器正是我们解决应用中性能问题所需的东西,但它们包含需我们考虑的一些要求。我们逐一了解其中的每一个要求。
一次针对多个对象的数据
假设我们的数据加载器收集了我们的query中涉及的所有不同密钥,并且准备发出数据请求。它接下来需要什么呢?
嗯,如果我们思考一下之前用于返回便利设施数据的 REST API 端点,我们会很快发现问题:现在,Listing.amenities
解析器依赖于ListingAPI
方法getAmenities
将每个列表 ID 单独发送到GET /listings/{listing_id}/amenities
端点,它仅为一个单个列表返回数据。
这让我们的数据加载器按预期工作有了第一个重要的要求:我们需要一个数据源能够同时解析多个对象的请求。在实践中,这意味着我们的数据加载器应该能够发送一串密钥(例如["listing-1", "listing-2", "listing-3"]
),并获取所有这些密钥的数据。
好消息是,我们的 REST API 中确实有不同的端点,我们可以使用它来请求多个列表的便利设施数据:GET /amenities/listings
它接受多个列表 ID,以一个称为ids
的query参数字符串的形式连接,一次性返回它们的所有数据。
GET /amenities/listings?ids=listing-1,listing-2,listing-3
每个键一个值
当数据加载器在请求中发送一串密钥时,它对提供数据的数据源有非常明确的期望:返回的对象数量决不能大于请求中发送的密钥数量。返回的值也应与请求的相应密钥具有相同的顺序。
让我们分解一下这个期望以及数据源如何满足它。
在解决查询的过程中,我们的解析器可能会调用数据加载器三次,向其传递三个键。数据加载器将它们分组到一个列表中(["listing-1", "listing-2", "listing-3"]
),然后使用它们来调用GET /amenities/listings
。它期望得到什么返回结果?嗯,它放入了一个列表,包含三个键;它期望返回一个包含不超过三个对象列表!
对于每个请求的键,我们期望我们的数据源返回一个便利设施列表。这是因为每个清单都可以具有多个与之关联的便利设施。因此,如果我们请求三个列表 ID 的便利设施数据,则我们的响应应该包含三个便利设施列表。
注意:数据加载器还期望每个返回的对象与原始请求中对应键的位置保持一致。例如,如果将“listing-1”作为第一个键发送,则其便利设施列表应该是响应中的第一个对象!
从那里,数据加载器处理将每个便利设施列表映射回请求它的键的逻辑。
数据加载器作用域
需要记住的最后一个重要要点是。必须在一次请求中限制数据加载器和它们在任何时间处理的键集合。因此,我们在下一课中的实现将确保每个请求都会创建一个新的 DataLoader
实例。
这意味着,如果我们运行一个 查询来列出数据,然后依次运行第二个查询,那么这两个查询中的键将不会被批量处理到一起。相反,每个查询将被单独解析。
明确了这些概念性要点之后,让我们将注意力重新转向代码。我们将更新我们的 Listing.amenities
解析器以在我们的 ListingAPI
类中调用一个 新的方法,并从数据加载器中获益!
练习
关键要点
- 数据加载器让我们可以将一个 ID(例如 ID)的列表批量处理在一个请求中,而不是为每个 ID 发送一个单独的请求。
- 在我们 数据源(另一个 API 或数据库)能够正常工作之前,数据加载器需要实现一个方法,该方法接受多个键(例如 ID),并返回多个对象。
- 数据加载器从 数据源接收到的对象数量不应超过数据加载器收集的键的数量。(例如,如果数据加载器请求 三个 listing 的数据,它最多只应收到 三个 listing 对象!)
接下来
我们已经了解了数据加载器及其解决我们应用程序中遇到问题的解决方案。我们还有一个新的 数据源 方法,该方法接受 多个清单 ID,并解析多个便利设施对象。接下来,我们将实现数据加载器逻辑,以便在单个请求中收集多个清单 ID。
分享你对本课程的问题和评论
本课程目前处于
你将需要一个 GitHub 帐户才能在下面发帖。还没有吗? 转而发布到我们的 Odyssey 论坛。