ClientConn 结构
ClientConn主要模块包括
- resolverWrapper ccResolverWrapper【协议解析器】
- csMgr connectivityStateManager【连接状态管理】
- blockingpicker pickerWrapper【从中获取连接】
- balancerWrapper ccBalancerWrapper【负载均衡包装器】
附源码:
type ClientConn struct {ctx context.Contextcancel context.CancelFunc// 连接的目标,基于配置来配置!target string// 根据Target解析出来的TargetparsedTarget resolver.Target// 认证,token之类的authority string// dialOptions 建立连接选线dopts dialOptions// 连接状态管理器csMgr *connectivityStateManager// 负载均衡 设置配置balancerBuildOpts balancer.BuildOptions// 负载均衡选择器,picker的时候会阻塞!,阻塞的pickerblockingpicker *pickerWrappermu sync.RWMutex// 包装了ClientConn和resolver 解析器resolverWrapper *ccResolverWrapper// 服务期配置的参数,弃用sc *ServiceConfig// 真实连接,里面包含transportconns map[*addrConn]struct{}// Keepalive parameter can be updated if a GoAway is received.// 保持连接参数mkp keepalive.ClientParameters// 当前负载均衡名字curBalancerName string// 包装了负载均衡的ClientConnbalancerWrapper *ccBalancerWrapper// 重试节流器retryThrottler atomic.Value// 首次Resolve的时候的事件,firstResolveEvent *grpcsync.Event// 唯一标识号channelzID int64 // channelz unique identification number// 存储调用成功、失败的个数czData *channelzData}
一、启动过程分析
1. 解析出resolver.Target
在conn过程中,首先会解析target【cc.parsedTarget = grpcutil.ParseTarget(cc.target)】,从中解析出resolver.Target,包含三部分:
type Target struct {
Scheme string // 方案
_Authority string // 认证
Endpoint string // 节点
_}
2. 根据【parsedTarget.Scheme】获取resolverBuilder
resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
3. 创建ccResolverWrapper【AddrConn】
// Build the resolver. and update StateChange rWrapper, err := newCCResolverWrapper(cc, resolverBuilder)
- 创建ccr:= &ccResolverWrapper{}
- 传入ccr,调用 rb.Build(cc.parsedTarget, ccr, rbo)
- build中,创建完Resolver后,根据自己的实现解析出[]resolver.Address,传入addrStrs回调ccr.UpdateState,将地址传入到ccr中。
ccr 调用 cc.updateResolverState -> cc.maybeApplyDefaultServiceConfig
4. 选择/创建负载均衡器
###################负载均衡
- cc.applyServiceConfigAndBalancer(emptyServiceConfig, addrs)
- 设置负载均衡器,cc.switchBalancer(newBalancerName)
- builder := balancer.Get(name)
cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts)
5. 更新负载均衡器状态【UpdateClientConnState,创建cc.NewSubConn】
ccBalancerWrapper.updateClientConnState(&balancer.ClientConnState{ResolverState: s, BalancerConfig: balCfg})
- 根据balancer版本,调用UpdateClientConnState 或HandleResolvedAddrs
- V2Balancer.UpdateClientConnState ,
- 创建subConn
- b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
- 调用 ccBalancerWrapper.NewSubConn【生成acBalancerWrapper】 -> cc.newAddrConn()【生成addrConn】
- b.sc.Connect() ->acbw.ac.connect(),调用回 addrConn.connect()
- 开启一个go 异步去连接 go ac.resetTransport()
- ac.updateConnectivityState(connectivity.Connecting, nil)
- newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline)
- ac.startHealthCheck(hctx),updateConnectivityState 协程改 Ready状态
ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr) -> cc.balancerWrapper.handleSubConnStateChange(sc, s, err)
6. 创建连接成功,更新连接状态[UpdateSubConnState]
ccb.scBuffer.Put(&scStateUpdate…)
- ccBalancerWrapper.watcher(), 调用 V2Balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
将连接改为Ready。 - lb.cc.UpdateState(balancer.State{ConnectivityState: lb.state, Picker: lb.picker})
- 调回ccBalancerWrapper.UpdateState()
- 调用ccb.cc.blockingpicker.updatePickerV2(s.Picker),加入picker
- ccb.cc.csMgr.updateState(s.ConnectivityState) // 更改状态为Ready
二、ClientConn原理梳解:
原理
开门见山来说,ClientConn 就是一个代理连接器,整合了 服务发现 Resolver;负载均衡Balancer;
然后抽象了State,在Dial过程中不断更新Resolver 和BalanceWrapper的State,从而更新csMgr 的State;
结构梳理

总结:
client启动过程:解析target,获取resolverBuilder,构建resolver传入 resolverWrapper,将解析出来的 ip地址和端口传给resolverWrapper,调用 clientConn的updateResolveState(),clientConn.updateResolveState()会判断有无负载均衡器,则创建负载均衡器,传入解析出来的地址,然后将负载均衡器给BalanceWrapper,然后调用 balanceWrapper的updateClientConnState() 创建subConnect,这里会根据ResolveState的地址来创建多个连接,连接进行tcp握手,http2.0升级后,最后会调用 clientConn 的UpdateSubConnState(),更新clientConn的状态。
