适用于 Java 的 Azure SDK 中的分页和迭代

本文概述了如何使用 Azure SDK for Java 分页和迭代功能高效高效地处理大型数据集。

Azure Java SDK 中的客户端库提供的许多操作会返回多个结果。 在这些情况下,Azure Java SDK 定义了一组可接受的返回类型,以确保开发人员体验通过一致性最大化。 使用的返回类型为 PagedIterable(用于同步 API)和 PagedFlux(用于异步 API)。 API 因用例的不同而略有不同,但从概念上讲,它们具有相同的要求:

  • 可以轻松地单独迭代集合中的每个元素,而无需手动分页或跟踪延续令牌。 PagedIterablePagedFlux 都通过迭代反序列化为给定类型 T 的分页响应来简化此任务。 PagedIterable 实现 Iterable 接口,并提供一个 API 来接收 Stream,同时 PagedFlux 提供一个 Flux。 在所有情况下,分页行为都是透明的,并且只要仍有结果需要迭代,迭代就会继续。

  • 可以逐页明确地进行迭代。 这样做可以让你更清楚地了解何时发出请求,并允许访问每页响应信息。 PagedIterablePagedFlux 都具有一些方法,这些方法将返回适合按页面而非单个元素进行迭代的类型。

本文在 Java Azure SDK 同步 API 和异步 API 之间进行拆分。 使用同步客户端时,你将看到同步迭代 API,在处理异步客户端时会看到异步迭代 API。

同步分页和迭代

本部分介绍同步 API。

迭代单个元素

如前所述,最常见的用例是逐个循环访问每个元素,而不是每个页面。 以下代码示例展示如何使用 PagedIterable API 来运用适合自己的迭代样式实现此功能。

使用 for-each 循环

由于 PagedIterable 实现了 Iterable,您可以通过以下示例来迭代元素:

PagedIterable<Secret> secrets = client.listSecrets();
for (Secret secret : secrets) {
   System.out.println("Secret is: " + secret);
}

使用流

由于 PagedIterable 其上定义了一个 stream() 方法,因此可以调用该方法以使用标准 Java 流 API,如以下示例所示:

client.listSecrets()
      .stream()
      .forEach(secret -> System.out.println("Secret is: " + secret));

使用迭代器

因为 PagedIterable 实现了 Iterable,它还具有一个 iterator() 方法,以允许使用 Java 迭代器编程样式,如以下示例所示:

Iterator<Secret> secrets = client.listSecrets().iterator();
while (it.hasNext()) {
   System.out.println("Secret is: " + it.next());
}

遍历页面

使用各个页面时,可以逐页处理,例如,当需要 HTTP 响应信息或续页标记对于保留迭代历史至关重要时。 无论你是按页面循环访问还是按每个项进行迭代,性能还是对服务进行的调用次数都没有任何差异。 基础实现按需加载下一页,如果随时取消订阅 PagedFlux ,则不会进一步调用该服务。

使用 for-each 循环

调用 listSecrets()时,会得到一个 PagedIterable,它具有 iterableByPage() API。 此 API 生成一个 Iterable<PagedResponse<Secret>> 而不是一个 Iterable<Secret>PagedResponse 提供响应元数据和继续令牌的访问权限,如以下示例所示:

Iterable<PagedResponse<Secret>> secretPages = client.listSecrets().iterableByPage();
for (PagedResponse<Secret> page : secretPages) {
   System.out.println("Response code: " + page.getStatusCode());
   System.out.println("Continuation Token: " + page.getContinuationToken());
   page.getElements().forEach(secret -> System.out.println("Secret value: " + secret))
}

还有一个 iterableByPage 重载,它接受延续令牌。 如果以后要返回到同一迭代点,则可以调用此重载。

使用流

以下示例演示该方法如何执行与上面所示相同的操作。 此 API 还具有延续令牌重载,用于稍后返回到相同的迭代点。

client.listSecrets()
      .streamByPage()
      .forEach(page -> {
          System.out.println("Response code: " + page.getStatusCode());
          System.out.println("Continuation Token: " + page.getContinuationToken());
          page.getElements().forEach(secret -> System.out.println("Secret value: " + secret))
      });

异步观察页面和单个元素

本部分介绍异步 API。 在异步 API 中,网络调用发生在与调用 subscribe()的主线程不同的线程中。 这意味着主线程可能会在结果可用之前终止。 由你负责确保在异步操作完成之前,应用程序不会退出。

观察各个元素

以下示例展示了 PagedFlux API 如何让你异步观察单个元素。 有多种方法可以订阅 Flux 类型。 有关详细信息,请参阅 Reactor 3 参考指南中的创建 Flux 或 Mono 并订阅它的简单方法。 此示例是其中有三个 lambda 表达式的一种,分别用于使用者、错误使用者和完整使用者。 拥有这三种情况是一种很好的做法,但在某些情况下,只需要有使用者,并且可能还有错误使用者。

asyncClient.listSecrets()
   .subscribe(secret -> System.out.println("Secret value: " + secret),
       ex -> System.out.println("Error listing secrets: " + ex.getMessage()),
       () -> System.out.println("Successfully listed all secrets"));

观察页面

以下示例展示了 PagedFlux API 如何让你异步观察每个页面,同样是通过使用 byPage() API 并提供使用者、错误使用者和完成使用者。

asyncClient.listSecrets().byPage()
  .subscribe(page -> {
          System.out.println("Response code: " + page.getStatusCode());
          System.out.println("Continuation Token: " + page.getContinuationToken());
          page.getElements().forEach(secret -> System.out.println("Secret value: " + secret))
      },
      ex -> System.out.println("Error listing pages with secret: " + ex.getMessage()),
      () -> System.out.println("Successfully listed all pages with secret"));

后续步骤

现在你已经熟悉了适用于 Java 的 Azure SDK 中的分页和迭代,请考虑查看 Azure SDK for Java 中的长时间运行操作。 长时间运行的操作是那些运行时间比大多数正常 HTTP 请求更长的操作,通常是因为它们需要在服务器端进行一些额外的处理。