1. 简介

在本教程中,我们将继续探索 Kubernetes Java Client。这一次,重点放在两个特性上:分页(Paging)和异步调用(Async Calls)

2. 分页机制

简单来说,分页 允许我们将一个大的结果集拆分成多个“块”或“页”来迭代处理——这也是该方法名称的由来。在 Kubernetes Java API 中,所有返回资源列表的方法都支持分页功能

这些方法通常包含两个可选参数:

  • limit:单次 API 调用最多返回的条目数
  • continue:一个 continuation token,用于告诉服务器从哪个位置开始返回结果

借助这两个参数,我们可以遍历任意数量的资源,而不会给服务端带来过大的压力。同时,客户端所需的内存也得到了控制。

下面是一个使用分页获取集群中所有 Pod 的示例代码:

ApiClient client = Config.defaultClient();
CoreV1Api api = new CoreV1Api(client);
String continuationToken = null;
do {
    V1PodList items = api.listPodForAllNamespaces(
      null,
      continuationToken, 
      null,
      null, 
      2, 
      null, 
      null,
      null,
      10,
      false);
    continuationToken = items.getMetadata().getContinue();
    items.getItems()
      .stream()
      .forEach((node) -> System.out.println(node.getMetadata()));
} while (continuationToken != null);

在这个例子中,listPodForAllNamespaces() 方法的第二个参数是 continue,第五个参数是 limit。第一次调用时,我们将 continue 设置为 null,表示这是分页请求序列的第一个请求。之后,每次从响应元数据中提取新的 continue 值,并将其用于下一次请求。

continue 返回值为 null 时,说明已经没有更多数据了,此时可以结束循环。

2.1. 分页注意事项 ⚠️

虽然分页机制看起来简单,但有几个细节需要注意:

不支持服务端排序:目前 API 不支持服务端排序,且短期内不太可能改变(见 GitHub Issue

除 continue 外的所有参数必须保持一致:否则可能导致分页异常

不要解析 continue token 的内容:它应被视为不透明的句柄,不要对其内容做任何假设

只能向前翻页:无法通过之前的 token 回退到前面的结果页

remainingItemCount 字段不可靠:这个字段在部分实现中可能为空或不准确

2.2. 列表数据一致性问题

由于 Kubernetes 集群是一个高度动态的环境,在分页读取过程中,某些资源可能会发生变化。那么在这种情况下,Kubernetes API 是如何保证一致性的呢?

根据 Kubernetes 文档,列表接口支持 resourceVersionresourceVersionMatch 参数,用于定义资源版本的选择策略。

但在分页场景下,行为始终是固定的:“Continue Token, Exact”

这意味着返回的资源版本与分页请求发起时的状态一致。这种做法虽然保证了数据一致性,但不会包含后续变更的内容。例如,在遍历大型集群的所有 Pod 时,一些 Pod 可能已经在中途终止了。

3. 异步调用

到目前为止,我们一直以同步方式使用 Kubernetes API。这种方式对于简单程序没问题,但从资源利用角度来看并不高效,因为它会阻塞调用线程直到收到并处理完响应。如果是在 GUI 线程中执行这类调用,将严重影响应用响应速度。

好消息是,该库支持基于回调的异步调用模式,能立即把控制权交还给调用者

查看 CoreV1Api 类你会发现,每一个同步方法(如 xxx())都有对应的异步版本 xxxAsync()。比如 listPodForAllNamespaces() 的异步方法就是 listPodForAllNamespacesAsync()。它们的参数基本相同,只是多了一个回调参数。

3.1. 回调接口详解

回调参数需要实现泛型接口 ApiCallback<T>,该接口包含四个方法:

  • onSuccess:仅在调用成功时触发,参数类型与同步方法返回值一致
  • onFailure:服务器调用失败或响应包含错误码时触发
  • onUploadProgress:上传进度更新时调用,可用于提供用户反馈
  • onDownloadProgress:下载进度更新时调用,作用同上

此外,异步调用不会直接返回结果,而是返回一个 OkHttpCall 实例(Kubernetes API 底层使用的 REST 客户端),可用于查询调用状态或提前取消操作。

3.2. 异步调用示例 ✅

显而易见,到处写回调会导致大量样板代码。为此,我们可以借助一个 异步辅助类 来简化操作:

// 启动异步调用
CompletableFuture<V1NodeList> p = AsyncHelper.doAsync(api,(capi,cb) ->
  capi.listNodeAsync(null, null, null, null, null, null, null, null, 10, false, cb)
);
p.thenAcceptAsync((nodeList) -> {
    nodeList.getItems()
      .stream()
      .forEach((node) -> System.out.println(node.getMetadata()));
});
// ... 在等待结果的同时做一些其他事情

在这个例子中,辅助类将异步调用包装为更标准的 CompletableFuture,方便与其他库(如 Reactor Project)集成。我们还添加了一个完成阶段,用于打印所有节点的元数据信息。

需要注意的是,在使用 Future 时仍要小心并发问题。实际运行时可以看到,即使是这段简单的代码,也会涉及至少三个线程:

  • 主线程:发起异步调用
  • OkHttp 的工作线程:负责 HTTP 请求
  • Completion 线程:处理回调结果

4. 总结

本文介绍了如何在 Kubernetes Java Client 中使用分页和异步调用功能。这些特性能够显著提升大规模操作的性能与用户体验。

一如既往,完整示例代码可在 GitHub 上找到。


原始标题:Paging and Async Calls with the Kubernetes API