bigywater.tech
Kubernetes · 14

watch bookmark:断线重连怎么避免全量 list

一个关心的对象很少变化,集群却一直在写——客户端断线重连时拿着一个又旧又没用的 resourceVersion,就会收到 410,只能把整个集合重新 list 一遍。bookmark 就是服务端定期告诉客户端“我现在到哪了”,这篇做一个能稳定复现 410 的对比实验。

59.7 秒
安静的对象上,带 allowWatchBookmarks 的 watcher 收到第一个 BOOKMARK 的时间
0 个事件
没有 bookmark 的 watcher 在 87 秒、7000 次别处写入期间收到的事件数
410 vs 成功
85 秒时从旧版本重连被拒绝,从 27 秒前的 BOOKMARK 版本重连成功

安静的对象与 bookmark

实测 · 本机执行输出
  持续在 noise 上写入,共 7000 次,87 秒;quiet 自己一次都没有变过
  不带 allowWatchBookmarks 的 watcher:收到 0 个事件(它所知道的最后版本一直是 41857)
  带 allowWatchBookmarks=true 的 watcher:收到 1 个事件,全是 BOOKMARK:
    第  59.7 秒 BOOKMARK resourceVersion=46796
  一个 BOOKMARK 事件的原文:{"type": "BOOKMARK", "object": {"kind": "ConfigMap", "apiVersion": "v1", "metadata": {"resourceVersion": "46796"}}}

实验里的 quiet 从不被修改,集群却在持续写别的对象。不带 allowWatchBookmarks 的 watcher 一个事件都收不到,它能记住的最后版本一直是创建时的 41857——集群的版本早就走远了。带 allowWatchBookmarks=true 的 watcher 在第 59.7 秒收到一个 BOOKMARK 事件:里面只有 kind 和 metadata.resourceVersion(46796),没有对象内容。它的含义是“到 46796 为止你没有漏掉任何事件”。

断线重连

实测 · 本机执行输出
  写到第 7000 次(85 秒)时,从 quiet 最后一次变化的版本 rv=41857 重连:ERROR 410,too old resource version: 41857 (42466)
  同一时刻,从最近收到的 BOOKMARK(rv=46796,是 27 秒前收到的)重连:成功,没有 410
  没有 bookmark 的客户端遇到 410 只能重新 list:得到 resourceVersion=48998

写到第 7000 次(85 秒)时模拟断线重连。从 quiet 最后一次变化的版本 41857 重连得到 ERROR 410: too old resource version: 41857 (42466)(括号里是 apiserver 缓存里最老的版本),客户端只能重新 list 整个集合再 watch;从 27 秒前收到的 BOOKMARK 版本 46796 重连则直接成功。对只关心少数对象、又运行在写入频繁的集群里的客户端,bookmark 能避免反复全量 list。client-go 的 reflector 默认就会带上 allowWatchBookmarks=true。

要注意旧版本多久会失效不是固定值:同一个脚本的另一次运行里,写了 10000 次、105 秒之后从旧版本重连仍然成功(缓存窗口随写入速率变化),所以不要依赖“某个时间点一定 410”。这里的对比是用循环等到它第一次出现 410 为止。

分步演示

bookmark 与重连
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0);两个 watcher 都是 kubectl get --raw "…configmaps?fieldSelector=metadata.name=quiet&resourceVersion=<创建时的版本>&watch=true",一个加 allowWatchBookmarks=true;noise 上的写入用 8 个线程并发的 kubectl patch(约 85 次/秒),每写 1000 次就用同样的 URL 试一次从 quiet 的旧版本重连,第一次出现 410 时再试从最近一个 BOOKMARK 的版本;版本号每次运行不同;每种情形各一次
    • 没有验证:BOOKMARK 的周期(只观察到约 60 秒一次,以及 watch 关闭前的那一个);同一个脚本另一次运行的结果(写 10000 次也没过期)说明窗口不固定;allowWatchBookmarks 对 list+watch 的 reflector 的完整流程;客户端对 BOOKMARK 事件的处理细节;watch 缓存关闭时的 bookmark 行为
    京ICP备2026067502号-1京公网安备11010502063487号