informer 是缓存+事件分发组合,非监听器;90% 事件丢失和空读源于误用 addeventhandler 或忽略 hassynced()。需 waitforcachesync 等待同步完成,优先使用 sharedindexinformer,deltafifo 不接受裸对象注入。

直接说结论:Informer 不是“监听器”,而是一套缓存+事件分发组合;用错 AddEventHandler 或忽略 HasSynced(),90% 的事件丢失和缓存空读问题就出在这两处。
为什么 AddEventHandler 注册后收不到事件?
常见现象:Informer 启动后没日志、AddFunc 从不触发、Get 返回 nil —— 很可能只是还没同步完。
-
AddEventHandler只注册回调,不保证立即生效;Informer 必须先完成首次List并填充本地缓存,才会开始分发事件 - 必须在启动 Informer 后、使用前显式等待
HasSynced()返回true,否则所有Get/List都可能读到空缓存 - 典型错误写法:
informer.Run(stopCh); informer.GetIndexer().GetByKey(...)—— 这里Run是异步的,GetByKey极大概率返回nil - 正确做法:用
cache.WaitForCacheSync等待同步完成,例如:cache.WaitForCacheSync(ctx.Done(), informer.HasSynced)
SharedIndexInformer 和 IndexInformer 的关键区别
别被名字绕晕:实际项目中几乎只用 SharedIndexInformer,它才是 client-go 的事实标准。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
IndexInformer是底层封装,不共享Reflector,每个实例都单独连 APIServer,浪费连接和带宽 -
SharedIndexInformer复用同一个Reflector,多个 Informer(如监听 Pod + ConfigMap)共用一个 Watch 连接,降低 APIServer 压力 - 你通过
sharedInformerFactory.Core().V1().Pods().Informer()拿到的,就是SharedIndexInformer - 它的
GetIndexer()返回Indexer接口,支持按 label、namespace 等自定义索引,但默认只建了namespace/name主键索引
DeltaFIFO 队列不是透明的,别手动塞对象
有人想绕过 List/Watch 直接往缓存“注入”测试数据,结果发现 AddEventHandler 完全不触发 —— 因为 DeltaFIFO 是事件入口,不是存储层。
-
DeltaFIFO只接收来自Reflector的Delta(Add/Update/Delete/Replaced/Sync),不接受裸对象 - 调用
informer.GetIndexer().Add(obj)只写缓存,不会生成 Delta,也不会触发任何事件回调 - 测试时想模拟事件,应使用
informer.GetStore().Add()+ 手动调用HandleDeltas(仅限单元测试),生产环境严禁这么做 - 真正需要“主动触发”的场景,应该走 Reconcile 循环或外部事件桥接(如 webhook → queue → controller)
最常被跳过的细节是:Informer 的生命周期必须与 Controller 的启动顺序严格对齐。缓存未就绪就查、事件未注册就 Run、StopCh 提前关闭 —— 这些边界条件在压测或集群波动时才会暴露,但修复成本远高于初始化时加几行等待逻辑。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










