github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterresolver/clusterresolver.go (about)

     1  /*
     2   *
     3   * Copyright 2019 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package clusterresolver contains EDS balancer implementation.
    20  package clusterresolver
    21  
    22  import (
    23  	"encoding/json"
    24  	"errors"
    25  	"fmt"
    26  
    27  	"github.com/hxx258456/ccgo/grpc/attributes"
    28  	"github.com/hxx258456/ccgo/grpc/balancer"
    29  	"github.com/hxx258456/ccgo/grpc/balancer/base"
    30  	"github.com/hxx258456/ccgo/grpc/connectivity"
    31  	"github.com/hxx258456/ccgo/grpc/internal/buffer"
    32  	"github.com/hxx258456/ccgo/grpc/internal/grpclog"
    33  	"github.com/hxx258456/ccgo/grpc/internal/grpcsync"
    34  	"github.com/hxx258456/ccgo/grpc/internal/pretty"
    35  	"github.com/hxx258456/ccgo/grpc/resolver"
    36  	"github.com/hxx258456/ccgo/grpc/serviceconfig"
    37  	"github.com/hxx258456/ccgo/grpc/xds/internal/balancer/priority"
    38  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    39  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource"
    40  )
    41  
    42  // Name is the name of the cluster_resolver balancer.
    43  const Name = "cluster_resolver_experimental"
    44  
    45  var (
    46  	errBalancerClosed = errors.New("cdsBalancer is closed")
    47  	newChildBalancer  = func(bb balancer.Builder, cc balancer.ClientConn, o balancer.BuildOptions) balancer.Balancer {
    48  		return bb.Build(cc, o)
    49  	}
    50  )
    51  
    52  func init() {
    53  	balancer.Register(bb{})
    54  }
    55  
    56  type bb struct{}
    57  
    58  // Build helps implement the balancer.Builder interface.
    59  func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
    60  	priorityBuilder := balancer.Get(priority.Name)
    61  	if priorityBuilder == nil {
    62  		logger.Errorf("priority balancer is needed but not registered")
    63  		return nil
    64  	}
    65  	priorityConfigParser, ok := priorityBuilder.(balancer.ConfigParser)
    66  	if !ok {
    67  		logger.Errorf("priority balancer builder is not a config parser")
    68  		return nil
    69  	}
    70  
    71  	b := &clusterResolverBalancer{
    72  		bOpts:    opts,
    73  		updateCh: buffer.NewUnbounded(),
    74  		closed:   grpcsync.NewEvent(),
    75  		done:     grpcsync.NewEvent(),
    76  
    77  		priorityBuilder:      priorityBuilder,
    78  		priorityConfigParser: priorityConfigParser,
    79  	}
    80  	b.logger = prefixLogger(b)
    81  	b.logger.Infof("Created")
    82  
    83  	b.resourceWatcher = newResourceResolver(b)
    84  	b.cc = &ccWrapper{
    85  		ClientConn:      cc,
    86  		resourceWatcher: b.resourceWatcher,
    87  	}
    88  
    89  	go b.run()
    90  	return b
    91  }
    92  
    93  func (bb) Name() string {
    94  	return Name
    95  }
    96  
    97  func (bb) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
    98  	var cfg LBConfig
    99  	if err := json.Unmarshal(c, &cfg); err != nil {
   100  		return nil, fmt.Errorf("unable to unmarshal balancer config %s into cluster-resolver config, error: %v", string(c), err)
   101  	}
   102  	return &cfg, nil
   103  }
   104  
   105  // ccUpdate wraps a clientConn update received from gRPC (pushed from the
   106  // xdsResolver).
   107  type ccUpdate struct {
   108  	state balancer.ClientConnState
   109  	err   error
   110  }
   111  
   112  // scUpdate wraps a subConn update received from gRPC. This is directly passed
   113  // on to the child balancer.
   114  type scUpdate struct {
   115  	subConn balancer.SubConn
   116  	state   balancer.SubConnState
   117  }
   118  
   119  type exitIdle struct{}
   120  
   121  // clusterResolverBalancer manages xdsClient and the actual EDS balancer implementation that
   122  // does load balancing.
   123  //
   124  // It currently has only an clusterResolverBalancer. Later, we may add fallback.
   125  type clusterResolverBalancer struct {
   126  	cc              balancer.ClientConn
   127  	bOpts           balancer.BuildOptions
   128  	updateCh        *buffer.Unbounded // Channel for updates from gRPC.
   129  	resourceWatcher *resourceResolver
   130  	logger          *grpclog.PrefixLogger
   131  	closed          *grpcsync.Event
   132  	done            *grpcsync.Event
   133  
   134  	priorityBuilder      balancer.Builder
   135  	priorityConfigParser balancer.ConfigParser
   136  
   137  	config          *LBConfig
   138  	configRaw       *serviceconfig.ParseResult
   139  	xdsClient       xdsclient.XDSClient    // xDS client to watch EDS resource.
   140  	attrsWithClient *attributes.Attributes // Attributes with xdsClient attached to be passed to the child policies.
   141  
   142  	child               balancer.Balancer
   143  	priorities          []priorityConfig
   144  	watchUpdateReceived bool
   145  }
   146  
   147  // handleClientConnUpdate handles a ClientConnUpdate received from gRPC. Good
   148  // updates lead to registration of EDS and DNS watches. Updates with error lead
   149  // to cancellation of existing watch and propagation of the same error to the
   150  // child balancer.
   151  func (b *clusterResolverBalancer) handleClientConnUpdate(update *ccUpdate) {
   152  	// We first handle errors, if any, and then proceed with handling the
   153  	// update, only if the status quo has changed.
   154  	if err := update.err; err != nil {
   155  		b.handleErrorFromUpdate(err, true)
   156  		return
   157  	}
   158  
   159  	b.logger.Infof("Receive update from resolver, balancer config: %v", pretty.ToJSON(update.state.BalancerConfig))
   160  	cfg, _ := update.state.BalancerConfig.(*LBConfig)
   161  	if cfg == nil {
   162  		b.logger.Warningf("xds: unexpected LoadBalancingConfig type: %T", update.state.BalancerConfig)
   163  		return
   164  	}
   165  
   166  	b.config = cfg
   167  	b.configRaw = update.state.ResolverState.ServiceConfig
   168  	b.resourceWatcher.updateMechanisms(cfg.DiscoveryMechanisms)
   169  
   170  	if !b.watchUpdateReceived {
   171  		// If update was not received, wait for it.
   172  		return
   173  	}
   174  	// If eds resp was received before this, the child policy was created. We
   175  	// need to generate a new balancer config and send it to the child, because
   176  	// certain fields (unrelated to EDS watch) might have changed.
   177  	if err := b.updateChildConfig(); err != nil {
   178  		b.logger.Warningf("failed to update child policy config: %v", err)
   179  	}
   180  }
   181  
   182  // handleWatchUpdate handles a watch update from the xDS Client. Good updates
   183  // lead to clientConn updates being invoked on the underlying child balancer.
   184  func (b *clusterResolverBalancer) handleWatchUpdate(update *resourceUpdate) {
   185  	if err := update.err; err != nil {
   186  		b.logger.Warningf("Watch error from xds-client %p: %v", b.xdsClient, err)
   187  		b.handleErrorFromUpdate(err, false)
   188  		return
   189  	}
   190  
   191  	b.logger.Infof("resource update: %+v", pretty.ToJSON(update.priorities))
   192  	b.watchUpdateReceived = true
   193  	b.priorities = update.priorities
   194  
   195  	// A new EDS update triggers new child configs (e.g. different priorities
   196  	// for the priority balancer), and new addresses (the endpoints come from
   197  	// the EDS response).
   198  	if err := b.updateChildConfig(); err != nil {
   199  		b.logger.Warningf("failed to update child policy's balancer config: %v", err)
   200  	}
   201  }
   202  
   203  // updateChildConfig builds a balancer config from eb's cached eds resp and
   204  // service config, and sends that to the child balancer. Note that it also
   205  // generates the addresses, because the endpoints come from the EDS resp.
   206  //
   207  // If child balancer doesn't already exist, one will be created.
   208  func (b *clusterResolverBalancer) updateChildConfig() error {
   209  	// Child was build when the first EDS resp was received, so we just build
   210  	// the config and addresses.
   211  	if b.child == nil {
   212  		b.child = newChildBalancer(b.priorityBuilder, b.cc, b.bOpts)
   213  	}
   214  
   215  	childCfgBytes, addrs, err := buildPriorityConfigJSON(b.priorities, b.config.XDSLBPolicy)
   216  	if err != nil {
   217  		return fmt.Errorf("failed to build priority balancer config: %v", err)
   218  	}
   219  	childCfg, err := b.priorityConfigParser.ParseConfig(childCfgBytes)
   220  	if err != nil {
   221  		return fmt.Errorf("failed to parse generated priority balancer config, this should never happen because the config is generated: %v", err)
   222  	}
   223  	b.logger.Infof("build balancer config: %v", pretty.ToJSON(childCfg))
   224  	return b.child.UpdateClientConnState(balancer.ClientConnState{
   225  		ResolverState: resolver.State{
   226  			Addresses:     addrs,
   227  			ServiceConfig: b.configRaw,
   228  			Attributes:    b.attrsWithClient,
   229  		},
   230  		BalancerConfig: childCfg,
   231  	})
   232  }
   233  
   234  // handleErrorFromUpdate handles both the error from parent ClientConn (from CDS
   235  // balancer) and the error from xds client (from the watcher). fromParent is
   236  // true if error is from parent ClientConn.
   237  //
   238  // If the error is connection error, it should be handled for fallback purposes.
   239  //
   240  // If the error is resource-not-found:
   241  // - If it's from CDS balancer (shows as a resolver error), it means LDS or CDS
   242  // resources were removed. The EDS watch should be canceled.
   243  // - If it's from xds client, it means EDS resource were removed. The EDS
   244  // watcher should keep watching.
   245  // In both cases, the sub-balancers will be receive the error.
   246  func (b *clusterResolverBalancer) handleErrorFromUpdate(err error, fromParent bool) {
   247  	b.logger.Warningf("Received error: %v", err)
   248  	if fromParent && xdsresource.ErrType(err) == xdsresource.ErrorTypeResourceNotFound {
   249  		// This is an error from the parent ClientConn (can be the parent CDS
   250  		// balancer), and is a resource-not-found error. This means the resource
   251  		// (can be either LDS or CDS) was removed. Stop the EDS watch.
   252  		b.resourceWatcher.stop()
   253  	}
   254  	if b.child != nil {
   255  		b.child.ResolverError(err)
   256  	} else {
   257  		// If eds balancer was never created, fail the RPCs with errors.
   258  		b.cc.UpdateState(balancer.State{
   259  			ConnectivityState: connectivity.TransientFailure,
   260  			Picker:            base.NewErrPicker(err),
   261  		})
   262  	}
   263  
   264  }
   265  
   266  // run is a long-running goroutine which handles all updates from gRPC and
   267  // xdsClient. All methods which are invoked directly by gRPC or xdsClient simply
   268  // push an update onto a channel which is read and acted upon right here.
   269  func (b *clusterResolverBalancer) run() {
   270  	for {
   271  		select {
   272  		case u := <-b.updateCh.Get():
   273  			b.updateCh.Load()
   274  			switch update := u.(type) {
   275  			case *ccUpdate:
   276  				b.handleClientConnUpdate(update)
   277  			case *scUpdate:
   278  				// SubConn updates are simply handed over to the underlying
   279  				// child balancer.
   280  				if b.child == nil {
   281  					b.logger.Errorf("xds: received scUpdate {%+v} with no child balancer", update)
   282  					break
   283  				}
   284  				b.child.UpdateSubConnState(update.subConn, update.state)
   285  			case exitIdle:
   286  				if b.child == nil {
   287  					b.logger.Errorf("xds: received ExitIdle with no child balancer")
   288  					break
   289  				}
   290  				// This implementation assumes the child balancer supports
   291  				// ExitIdle (but still checks for the interface's existence to
   292  				// avoid a panic if not).  If the child does not, no subconns
   293  				// will be connected.
   294  				if ei, ok := b.child.(balancer.ExitIdler); ok {
   295  					ei.ExitIdle()
   296  				}
   297  			}
   298  		case u := <-b.resourceWatcher.updateChannel:
   299  			b.handleWatchUpdate(u)
   300  
   301  		// Close results in cancellation of the EDS watch and closing of the
   302  		// underlying child policy and is the only way to exit this goroutine.
   303  		case <-b.closed.Done():
   304  			b.resourceWatcher.stop()
   305  
   306  			if b.child != nil {
   307  				b.child.Close()
   308  				b.child = nil
   309  			}
   310  			// This is the *ONLY* point of return from this function.
   311  			b.logger.Infof("Shutdown")
   312  			b.done.Fire()
   313  			return
   314  		}
   315  	}
   316  }
   317  
   318  // Following are methods to implement the balancer interface.
   319  
   320  // UpdateClientConnState receives the serviceConfig (which contains the
   321  // clusterName to watch for in CDS) and the xdsClient object from the
   322  // xdsResolver.
   323  func (b *clusterResolverBalancer) UpdateClientConnState(state balancer.ClientConnState) error {
   324  	if b.closed.HasFired() {
   325  		b.logger.Warningf("xds: received ClientConnState {%+v} after clusterResolverBalancer was closed", state)
   326  		return errBalancerClosed
   327  	}
   328  
   329  	if b.xdsClient == nil {
   330  		c := xdsclient.FromResolverState(state.ResolverState)
   331  		if c == nil {
   332  			return balancer.ErrBadResolverState
   333  		}
   334  		b.xdsClient = c
   335  		b.attrsWithClient = state.ResolverState.Attributes
   336  	}
   337  
   338  	b.updateCh.Put(&ccUpdate{state: state})
   339  	return nil
   340  }
   341  
   342  // ResolverError handles errors reported by the xdsResolver.
   343  func (b *clusterResolverBalancer) ResolverError(err error) {
   344  	if b.closed.HasFired() {
   345  		b.logger.Warningf("xds: received resolver error {%v} after clusterResolverBalancer was closed", err)
   346  		return
   347  	}
   348  	b.updateCh.Put(&ccUpdate{err: err})
   349  }
   350  
   351  // UpdateSubConnState handles subConn updates from gRPC.
   352  func (b *clusterResolverBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
   353  	if b.closed.HasFired() {
   354  		b.logger.Warningf("xds: received subConn update {%v, %v} after clusterResolverBalancer was closed", sc, state)
   355  		return
   356  	}
   357  	b.updateCh.Put(&scUpdate{subConn: sc, state: state})
   358  }
   359  
   360  // Close closes the cdsBalancer and the underlying child balancer.
   361  func (b *clusterResolverBalancer) Close() {
   362  	b.closed.Fire()
   363  	<-b.done.Done()
   364  }
   365  
   366  func (b *clusterResolverBalancer) ExitIdle() {
   367  	b.updateCh.Put(exitIdle{})
   368  }
   369  
   370  // ccWrapper overrides ResolveNow(), so that re-resolution from the child
   371  // policies will trigger the DNS resolver in cluster_resolver balancer.
   372  type ccWrapper struct {
   373  	balancer.ClientConn
   374  	resourceWatcher *resourceResolver
   375  }
   376  
   377  func (c *ccWrapper) ResolveNow(resolver.ResolveNowOptions) {
   378  	c.resourceWatcher.resolveNow()
   379  }