openWebSF 包含兩種版本的服務發現與負載均衡:
1: 老版本的
2. experimental API 版本的
詳情見github: https://github.com/ybdx/openWebSF
客戶端鏈接方式:
grpc.DialContext -> newCCResolverWrapper -> Build(這個是resolver的build) -> r.cc.NewAddress -> ccr.cc.handleResolvedAddrs -> newCCBalancerWrapper -> ccb.watcher() and Build(這個是balancer的build)
下面將詳細解釋:
首先,看撥號函數 DialContext:
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
cc := &ClientConn{
target: target,
csMgr: &connectivityStateManager{},
conns: make(map[*addrConn]struct{}),
dopts: defaultDialOptions(),
blockingpicker: newPickerWrapper(),
czData: new(channelzData),
firstResolveEvent: grpcsync.NewEvent(),
}
...
// 初始化opt
...
// 設置resolver, 判斷使用哪種resolver是通過註冊地址前面的scheme來決定的
if cc.dopts.resolverBuilder == nil {
// Only try to parse target when resolver builder is not already set.
cc.parsedTarget = parseTarget(cc.target)
grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme)
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme)
if cc.dopts.resolverBuilder == nil {
// If resolver builder is still nil, the parse target's scheme is
// not registered. Fallback to default resolver and set Endpoint to
// the original unparsed target.
grpclog.Infof("scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme)
cc.parsedTarget = resolver.Target{
Scheme: resolver.GetDefaultScheme(),
Endpoint: target,
}
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme)
}
} else {
cc.parsedTarget = resolver.Target{Endpoint: target}
}
...
// Build the resolver.
rWrapper, err := newCCResolverWrapper(cc)
if err != nil {
return nil, fmt.Errorf("failed to build resolver: %v", err)
}
cc.mu.Lock()
cc.resolverWrapper = rWrapper
cc.mu.Unlock()
// A blocking dial blocks until the clientConn is ready.
...
return cc, nil
}
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
rb := cc.dopts.resolverBuilder
if rb == nil {
return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
}
ccr := &ccResolverWrapper{
cc: cc,
addrCh: make(chan []resolver.Address, 1),
scCh: make(chan string, 1),
done: make(chan struct{}),
}
var err error
// 調用resolver Build函數
ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{DisableServiceConfig: cc.dopts.disableServiceConfig})
if err != nil {
return nil, err
}
return ccr, nil
}
grpc老版本:
// resolver Build
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
r := &passthroughResolver{
target: target,
cc: cc,
}
r.start()
return r, nil
}
func (r *passthroughResolver) start() {
r.cc.NewAddress([]resolver.Address{{Addr: r.target.Endpoint}})
}
// 調用r.cc.NewAddress, NewAddress中的地址可以是服務的地址,也可以是註冊中心的地址,不同的地址,對應HandleResolvedAddrs()功能不同,如果是服務地址,則直接調用b.cc.NewSubConn, 如果是註冊地址,則需要在balancer對應中去發現地址
調用NewAddress -> handleResolvedAddrs -> newCCBalancerWrapper and handleResolvedAddrs
// newCCBalancerWrapper
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
ccb := &ccBalancerWrapper{
cc: cc,
stateChangeQueue: newSCStateUpdateBuffer(),
resolverUpdateCh: make(chan *resolverUpdate, 1),
done: make(chan struct{}),
subConns: make(map[*acBalancerWrapper]struct{}),
}
go ccb.watcher()
ccb.balancer = b.Build(ccb, bopts)
return ccb
}
老版本對應的Build (NewAddress傳遞的是註冊中心的地址)
func (bwb *balancerWrapperBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
targetAddr := cc.Target()
targetSplitted := strings.Split(targetAddr, ":///")
if len(targetSplitted) >= 2 {
targetAddr = targetSplitted[1]
}
// 此處是調用Balancer中start function去發現服務ip, ip傳遞過來的方式是通過Balancer
// 中Notify()返回的channal, bw.lbWatcher收到channal並通過bw.cc.NewSubConn創建連接
bwb.b.Start(targetAddr, BalancerConfig{
DialCreds: opts.DialCreds,
Dialer: opts.Dialer,
})
_, pickfirst := bwb.b.(*pickFirst)
bw := &balancerWrapper{
balancer: bwb.b,
pickfirst: pickfirst,
cc: cc,
targetAddr: targetAddr,
startCh: make(chan struct{}),
conns: make(map[resolver.Address]balancer.SubConn),
connSt: make(map[balancer.SubConn]*scState),
csEvltr: &balancer.ConnectivityStateEvaluator{},
state: connectivity.Idle,
}
cc.UpdateBalancerState(connectivity.Idle, bw)
go bw.lbWatcher()
return bw
}
// start function
func (rr *roundRobin) Start(target string, config grpc.BalancerConfig) error {
rr.mu.Lock()
defer rr.mu.Unlock()
if rr.done {
return grpc.ErrClientConnClosing
}
if rr.r == nil {
// resolver can't be nil, the roundrobin_v1 is just use when the resolver is exist
return errors.New("resolver can't be nil")
}
// 調用resolver的Resolve(),註冊zk並創建zk client連接
w, err := rr.r.Resolve(target)
if err != nil {
return err
}
rr.w = w
rr.addrCh = make(chan []grpc.Address)
go func() {
for {
// 獲取zk註冊中心服務地址,並通過Notify()函數傳遞服務地址
if err := rr.watchAddrUpdates(); err != nil {
return
}
}
}()
return nil
}
grpc expermental 版本:
// resolver Build (自己實現的), 在該函數中實現了zk client 創建, 服務發現
func (zkb *zookeeperBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
r := &zookeeperResolver{
target: target,
cc: cc,
serviceName: zkb.name,
}
if nil == r.zk {
if cli, err:= zk.New(target.Endpoint); err != nil {
logrus.Fatalln("libkv NewStore failed:", err)
return nil, err
} else {
r.zk = cli
}
}
go r.watch()
return r, nil
}
func (r *zookeeperResolver) watch() {
prefix := utils.ServicePrefix(r.serviceName)
if pairs, err := r.zk.List(prefix); err == store.ErrKeyNotFound {
logrus.Errorf("watcher list %s failed, error: %v", prefix, err)
time.Sleep(3 * time.Second)
return
} else if err != nil {
logrus.Errorf("watcher list %s failed, error: %v", prefix, err)
return
} else {
addrs := make([]resolver.Address, 0)
for _, v := range pairs {
addr, metadata, err := getServerInfo(v)
if err != nil {
continue
}
addrs = append(addrs, resolver.Address{
Addr: addr,
Metadata: metadata,
Type: resolver.Backend,
})
}
r.cc.NewAddress(addrs)
}
stopCh := make(chan struct{})
if event, err := r.zk.WatchTree(prefix, stopCh); err != nil {
logrus.Errorf("watcher watchtree key %s failed, error: %v\n", prefix, err)
return
} else {
addrs := getUpdates(event)
r.cc.NewAddress(addrs)
}
}
調用NewAddress -> handleResolvedAddrs -> newCCBalancerWrapper and handleResolvedAddrs
新版本對應的Build(NewAddress傳遞的是註冊中心發現的服務的地址)
// 此處可以更改,新版本服務發現可以在這個地方實現,然後直接調用NewAddress函數
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
r := &passthroughResolver{
target: target,
cc: cc,
}
r.start()
return r, nil
}
// 此處地址是服務地址
func (r *passthroughResolver) start() {
r.cc.NewAddress([]resolver.Address{{Addr: r.target.Endpoint}})
}