* * Copyright 2016 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http:www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *

package grpclb

import (
	
	
	

	
	
)
The parent ClientConn should re-resolve when grpclb loses connection to the remote balancer. When the ClientConn inside grpclb gets a TransientFailure, it calls lbManualResolver.ResolveNow(), which calls parent ClientConn's ResolveNow, and eventually results in re-resolve happening in parent ClientConn's resolver (DNS for example). parent ClientConn +-----------------------------------------------------------------+ | parent +---------------------------------+ | | DNS ClientConn | grpclb | | | resolver balancerWrapper | | | | + + | grpclb grpclb | | | | | | ManualResolver ClientConn | | | | | | + + | | | | | | | | Transient | | | | | | | | Failure | | | | | | | <--------- | | | | | | <--------------- | ResolveNow | | | | | <--------- | ResolveNow | | | | | | | ResolveNow | | | | | | | | | | | | | | | + + | + + | | | +---------------------------------+ | +-----------------------------------------------------------------+
lbManualResolver is used by the ClientConn inside grpclb. It's a manual resolver with a special ResolveNow() function. When ResolveNow() is called, it calls ResolveNow() on the parent ClientConn, so when grpclb client lose contact with remote balancers, the parent ClientConn's resolver will re-resolve.
ResolveNow calls resolveNow on the parent ClientConn.
Close is a noop for Resolver.
func (*lbManualResolver) () {}
UpdateState calls cc.UpdateState.
lbCacheClientConn is a wrapper balancer.ClientConn with a SubConn cache. SubConns will be kept in cache for subConnCacheTime before being removed. Its new and remove methods are updated to do cache first.
subConnCache only keeps subConns that are being deleted.
	subConnCache  map[resolver.Address]*subConnCacheEntry
	subConnToAddr map[balancer.SubConn]resolver.Address
}

type subConnCacheEntry struct {
	sc balancer.SubConn

	cancel        func()
	abortDeleting bool
}

func ( balancer.ClientConn) *lbCacheClientConn {
	return &lbCacheClientConn{
		cc:            ,
		timeout:       subConnCacheTime,
		subConnCache:  make(map[resolver.Address]*subConnCacheEntry),
		subConnToAddr: make(map[balancer.SubConn]resolver.Address),
	}
}

func ( *lbCacheClientConn) ( []resolver.Address,  balancer.NewSubConnOptions) (balancer.SubConn, error) {
	if len() != 1 {
		return nil, fmt.Errorf("grpclb calling NewSubConn with addrs of length %v", len())
	}
	 := [0]
	.Metadata = nil

	.mu.Lock()
	defer .mu.Unlock()
If entry is in subConnCache, the SubConn was being deleted. cancel function will never be nil.
		.cancel()
		delete(.subConnCache, )
		return .sc, nil
	}

	,  := .cc.NewSubConn(, )
	if  != nil {
		return nil, 
	}

	.subConnToAddr[] = 
	return , nil
}

func ( *lbCacheClientConn) ( balancer.SubConn) {
	.mu.Lock()
	defer .mu.Unlock()
	,  := .subConnToAddr[]
	if ! {
		return
	}

	if ,  := .subConnCache[];  {
This could happen if NewSubConn was called multiple times for the same address, and those SubConns are all removed. We remove sc immediately here.
			delete(.subConnToAddr, )
			.cc.RemoveSubConn()
		}
		return
	}

	 := &subConnCacheEntry{
		sc: ,
	}
	.subConnCache[] = 

	 := time.AfterFunc(.timeout, func() {
		.mu.Lock()
		defer .mu.Unlock()
		if .abortDeleting {
			return
		}
		.cc.RemoveSubConn()
		delete(.subConnToAddr, )
		delete(.subConnCache, )
	})
	.cancel = func() {
If stop was not successful, the timer has fired (this can only happen in a race). But the deleting function is blocked on ccc.mu because the mutex was held by the caller of this function. Set abortDeleting to true to abort the deleting function. When the lock is released, the deleting function will acquire the lock, check the value of abortDeleting and return.
			.abortDeleting = true
		}
	}
}

func ( *lbCacheClientConn) ( balancer.State) {
	.cc.UpdateState()
}

func ( *lbCacheClientConn) () {
Only cancel all existing timers. There's no need to remove SubConns.
	for ,  := range .subConnCache {
		.cancel()
	}
	.mu.Unlock()