第 6 章:用队列组织持续工作
观察与执行分开
informer 负责列出并持续观察 GatewayInstance,事件处理器只把键加入队列。worker 才执行 API 请求和协调逻辑。若把耗时操作写进事件回调,会阻塞事件消费,使大量无关对象的观察延迟扩大,也难以控制失败重试。
随书使用 client-go 的动态 informer 和带类型的限速队列。动态客户端避免为这个小项目引入代码生成链,但对象字段读取需要自己核对类型。复杂成熟 API 往往更适合生成类型与客户端,以获得编译期约束;选动态实现不表示无结构 JSON 在所有规模下都更合适。
启动时必须等待缓存同步完成。虽然 Reconcile 通过直接 API GET 读取权威对象,主资源的初次发现仍依赖 informer 的 list 和事件分发。没有完成初始同步就宣称控制器已就绪,会让操作者无法区分“还未发现对象”与“已经处理全部对象”。
Done、Forget 与重试
每次 Get 到键之后都应调用 Done,让队列知道本轮处理结束。失败时 AddRateLimited 将键按退避规则重新加入;成功时 Forget 清除累计失败计数。忘记 Done 会使键的后续处理受阻,忘记 Forget 则可能让已经恢复的对象仍带着很长重试延迟。
本项目只有一个 worker,因此同一进程内没有两个协调函数同时执行。但 API 仍可能被用户、Deployment 控制器和准入组件改变,单 worker 并不消除并发。队列提供工作调度,resourceVersion 提供对象写入保护,两者职责不同。
错误返回会被重试,但并不是每种错误都适合无限重试。生产系统可以把无效引用、无权限和外部暂时不可用分成不同状态与重试策略。当前实验保留统一退避,使代码保持可读;读者应把错误分类视为后续工程能力,而不是根据队列名称推断它已具备完整故障治理。
为什么还要周期复查
主资源变化能唤醒控制器,但误删 Service 不会改变 GatewayInstance。本项目没有为 Deployment 和 Service 建立额外 watch,而是在成功后每秒重排仍然存在的主资源,从而最终发现子资源缺失或漂移。它用额外 API 读取换取了较小实现规模。
这个方案适合少量教学实例,不适合直接复制到大规模集群。若有十万实例,即使没有任何变化,也会产生持续扫描压力。生产设计通常观察拥有的子资源,并根据 owner reference 映射回主键,同时保留较低频率的兜底复查。事件驱动和周期扫描可以互补,但频率需要与对象规模、恢复目标和 API 预算一起计算。
删除完成后不能继续每秒重排一个已经消失的键。Run 在成功后确认对象仍存在,NotFound 则停止周期安排。否则每删除一个实例都会永久增加一条无效工作,长时间运行后队列和 API 请求量可能被历史对象拖累。
丢失事件与重新列出
watch 不是永久可靠的连接。网络中断、资源版本过旧和 API server 重启都会导致重新建立观察。informer 封装了大部分 list/watch 重连行为,但协调函数仍必须接受重复事件,并以当前对象为准。正确性不应依赖恰好收到某一次 Update。
控制器重启实验会真实终止 CLI 进程,在它停止期间改变 spec,再启动新进程。新 informer 的初始 list 必须发现已有实例,并把数据面副本推进到新的目标。这证明当前进程启动路径能恢复既有工作,不代表多实例故障切换或控制面断电后的完整恢复。
公平性与背压
一个坏对象反复失败时,限速队列能降低它对 API server 的冲击;它不能自动限制每个外部依赖的并发,也不能保证租户公平。增加 worker 前,应先衡量队列长度、处理时延和外部 API 限额,再决定是否需要按租户或资源类型分区。
练习是估算一千个实例每秒兜底复查的请求量:一轮包含主对象、外部登记、Deployment、Service 和状态读取,实际成本远超过一千次请求。把协调次数乘以每轮请求数,再考虑 API 限流,比只观察 CPU 利用率更能反映控制器扩容的瓶颈。
推演案例:一个慢对象拖住整条队列
假设外部登记查询需要两分钟,而当前只有一个worker。即使其余实例都正常,它们的删除与修复也会等待。提高worker数量可能改善吞吐,但同时放大外部连接数和竞争,不能作为唯一答案。
先给外部调用设置有界超时,再把未完成状态表达为稍后重试,避免在一次协调内无限等待。随后根据真实延迟分布决定worker并发,给每个依赖单独限制连接和请求速率。队列背压与外部调用预算应一起设计,否则系统会把积压从队列转移到远端服务。
随书代码的Kubernetes调用受context控制,测试也设置总截止时间;它没有实现独立外部HTTP依赖。因此不能把这一推演表述成已完成外部超时治理。扩展时应先增加一个可控慢服务,在有限并发下观察其他实例的完成时间,再根据证据确定隔离策略。