代理连接生命周期
把第 2 周串成一个完整故事:一个 Envoy 从连上 istiod、拿初始配置、收增量更新、到断开的全过程。这是控制面和数据面协作的端到端复盘。收官第 2 周。
全景六阶段
① 建立连接
Envoy(经 pilot-agent,Day 12)拨号 istiod :15012(mTLS)或 :15010(明文)的 ADS gRPC,调 StreamAggregatedResources(ads.go:182)。ready/限流/认证通过后 newConnection → xds.Stream(con),起 Receive 协程,阻塞等首请求(Day 06)。
② 首请求初始化
# Receive 收到首请求(必带 Node.Id)→ initConnection(ads.go:239):
# initProxyMetadata:解析 node id → model.Proxy
# proxy.LastPushContext = globalPushContext() # 保证 push context 单调
# addCon 注册进 adsClients(此后才被 push 遍历到)
# computeProxyState:算 SidecarScope/Gateway/labels(Day 05)
# MarkInitialized() → 主循环开始服务
addCon(注册进连接表)和 initializeProxy(算 proxy 状态)的顺序很讲究——注释(ads.go:265-269)专门解释了这个竞态。如果先注册再初始化,可能有推送来了但 proxy 状态还没算好;如果先初始化再注册,可能初始化期间的配置变更漏掉。Istio 的处理保证了"注册后一定能被推、且推的时候状态已就绪"。这种时序细节是分布式系统里最容易出 bug 的地方——读源码时看到这类注释要格外留意,它们往往是踩过坑的经验结晶。③ 初始配置下发
Envoy 依次发 CDS/LDS/EDS/RDS 请求;主循环从 reqChan 取出 → processRequest → ShouldRespond 判定"初始请求"(Day 06)→ pushXds → 对应 Generator(Day 07-08)→ BuildClusters/BuildListeners… → Send。Envoy 收到后回 ACK。
PushOrder,ads.go:499):CDS → EDS → LDS → RDS → SDS——因为 Envoy 有依赖(Listener 引用 Route、Route 引用 Cluster、Cluster 引用 Endpoint)。先发被依赖的,再发依赖它的,否则 Envoy 会收到"引用了还不存在的资源"的配置。每次下发 Envoy 都回 ACK(带 nonce)。Cluster(后端池)是地基 → 先发(CDS)Endpoint(池里的机器)填进 Cluster → 再发(EDS)Listener(门)会引用 Route → 发(LDS)Route(路牌)会指向 Cluster → 最后发(RDS)反过来先发 Route,它指向的 Cluster 还不存在 → Envoy 拿到"悬空引用",流量瞬间黑洞。这就是 PushOrder 存在的意义。
④ 增量更新
集群里某 VirtualService 变更 → configHandler → ConfigUpdate → pushChannel → debounce(100ms 合并)→ Push(全量时重算/复用 PushContext)→ AdsPushAll → StartPush 遍历所有连接入 PushQueue(Day 09)。
doSendPushes 出队 → Event 送入连接 pushChannel → 主循环 pushConnection:ProxyNeedsPush 判该 proxy 相关否 → 相关则按 PushOrder 逐类型 pushXds。Envoy 再 ACK。
ProxyNeedsPush 让不相关的 proxy 被跳过——改 payment 的路由,只有调用 payment 的 Envoy 收到更新。⑤ 端点高频更新
Pod 扩缩容 → EDSUpdate(eds.go:47)更新 endpoint 分片 → 触发 push;EDS 走独立缓存路径(Day 07),多数情况只重算该 service 的 ClusterLoadAssignment,且可 SotW 下增量推送(Day 06 通配/非通配)。
⑥ 断开清理
流关闭 → Receive 返回 → Stream 退出 → defer ctx.Close() → closeConnection(ads.go:283):removeCon 从 adsClients 删除 + WorkloadEntryController.OnDisconnect。
OnConnect 创建 WorkloadEntry(表示"这个 VM 上线了"),断开时 OnDisconnect 清理("VM 下线了")。于是 VM 的生命周期和它的 xDS 连接绑定——连着就在网格里,断了就自动摘除。这让非 K8s 的 VM 也能像 Pod 一样被自动管理(Day 04 的 K8s+VM 混合网格)。断开清理不只是删连接,还要处理这些副作用。第 2 周收官 🎓
🧠 第 2 周(xDS 生成)你已掌握
- Day 06 DiscoveryServer:ADS 流、收发分离、ShouldRespond 状态机
- Day 07 配置生成:ConfigGenerator、生成器薄封装、EDS 独立路径
- Day 08 三大 Builder:Listener/Cluster/Route 怎么构造 Envoy 对象
- Day 09 Push 模型:ConfigUpdate → debounce → PushQueue → 三级优化
- Day 10 连接生命周期:连接→初始化→初始下发→增量→端点→断开
你现在完全理解了 istiod 的核心——怎么把配置翻译成 xDS 并高效推送给每个 Envoy。第 1 周(数据基础)+ 第 2 周(xDS 生成)= istiod 控制面的完整闭环。第 3 周(已学)讲 sidecar 落地与安全,第 4 周讲遥测、EnvoyFilter、安装。
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/istio
grep -n 'func.*initConnection\|func.*closeConnection\|PushOrder' pilot/pkg/xds/ads.go
sed -n '445,476p' architecture/networking/pilot.md # 连接生命周期文档