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 文档,列表接口支持 resourceVersion
和 resourceVersionMatch
参数,用于定义资源版本的选择策略。
但在分页场景下,行为始终是固定的:“Continue Token, Exact”。
这意味着返回的资源版本与分页请求发起时的状态一致。这种做法虽然保证了数据一致性,但不会包含后续变更的内容。例如,在遍历大型集群的所有 Pod 时,一些 Pod 可能已经在中途终止了。
3. 异步调用
到目前为止,我们一直以同步方式使用 Kubernetes API。这种方式对于简单程序没问题,但从资源利用角度来看并不高效,因为它会阻塞调用线程直到收到并处理完响应。如果是在 GUI 线程中执行这类调用,将严重影响应用响应速度。
✅ 好消息是,该库支持基于回调的异步调用模式,能立即把控制权交还给调用者。
查看 CoreV1Api
类你会发现,每一个同步方法(如 xxx()
)都有对应的异步版本 xxxAsync()
。比如 listPodForAllNamespaces()
的异步方法就是 listPodForAllNamespacesAsync()
。它们的参数基本相同,只是多了一个回调参数。
3.1. 回调接口详解
回调参数需要实现泛型接口 ApiCallback<T>
,该接口包含四个方法:
- onSuccess:仅在调用成功时触发,参数类型与同步方法返回值一致
- onFailure:服务器调用失败或响应包含错误码时触发
- onUploadProgress:上传进度更新时调用,可用于提供用户反馈
- onDownloadProgress:下载进度更新时调用,作用同上
此外,异步调用不会直接返回结果,而是返回一个 OkHttp 的 Call
实例(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 上找到。