基於zookeeper和grpc的服務註冊與發現(go)

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}})
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章