gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/resolver/watch_service.go (about)

     1  /*
     2   *
     3   * Copyright 2020 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 resolver
    20  
    21  import (
    22  	"fmt"
    23  	"sync"
    24  	"time"
    25  
    26  	"gitee.com/ks-custle/core-gm/grpc/internal/grpclog"
    27  	"gitee.com/ks-custle/core-gm/grpc/internal/pretty"
    28  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/clusterspecifier"
    29  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient"
    30  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource"
    31  )
    32  
    33  // serviceUpdate contains information received from the LDS/RDS responses which
    34  // are of interest to the xds resolver. The RDS request is built by first
    35  // making a LDS to get the RouteConfig name.
    36  type serviceUpdate struct {
    37  	// virtualHost contains routes and other configuration to route RPCs.
    38  	virtualHost *xdsresource.VirtualHost
    39  	// clusterSpecifierPlugins contains the configurations for any cluster
    40  	// specifier plugins emitted by the xdsclient.
    41  	clusterSpecifierPlugins map[string]clusterspecifier.BalancerConfig
    42  	// ldsConfig contains configuration that applies to all routes.
    43  	ldsConfig ldsConfig
    44  }
    45  
    46  // ldsConfig contains information received from the LDS responses which are of
    47  // interest to the xds resolver.
    48  type ldsConfig struct {
    49  	// maxStreamDuration is from the HTTP connection manager's
    50  	// common_http_protocol_options field.
    51  	maxStreamDuration time.Duration
    52  	httpFilterConfig  []xdsresource.HTTPFilter
    53  }
    54  
    55  // watchService uses LDS and RDS to discover information about the provided
    56  // serviceName.
    57  //
    58  // Note that during race (e.g. an xDS response is received while the user is
    59  // calling cancel()), there's a small window where the callback can be called
    60  // after the watcher is canceled. The caller needs to handle this case.
    61  func watchService(c xdsclient.XDSClient, serviceName string, cb func(serviceUpdate, error), logger *grpclog.PrefixLogger) (cancel func()) {
    62  	w := &serviceUpdateWatcher{
    63  		logger:      logger,
    64  		c:           c,
    65  		serviceName: serviceName,
    66  		serviceCb:   cb,
    67  	}
    68  	w.ldsCancel = c.WatchListener(serviceName, w.handleLDSResp)
    69  
    70  	return w.close
    71  }
    72  
    73  // serviceUpdateWatcher handles LDS and RDS response, and calls the service
    74  // callback at the right time.
    75  type serviceUpdateWatcher struct {
    76  	logger      *grpclog.PrefixLogger
    77  	c           xdsclient.XDSClient
    78  	serviceName string
    79  	ldsCancel   func()
    80  	serviceCb   func(serviceUpdate, error)
    81  	lastUpdate  serviceUpdate
    82  
    83  	mu        sync.Mutex
    84  	closed    bool
    85  	rdsName   string
    86  	rdsCancel func()
    87  }
    88  
    89  func (w *serviceUpdateWatcher) handleLDSResp(update xdsresource.ListenerUpdate, err error) {
    90  	w.logger.Infof("received LDS update: %+v, err: %v", pretty.ToJSON(update), err)
    91  	w.mu.Lock()
    92  	defer w.mu.Unlock()
    93  	if w.closed {
    94  		return
    95  	}
    96  	if err != nil {
    97  		// We check the error type and do different things. For now, the only
    98  		// type we check is ResourceNotFound, which indicates the LDS resource
    99  		// was removed, and besides sending the error to callback, we also
   100  		// cancel the RDS watch.
   101  		if xdsresource.ErrType(err) == xdsresource.ErrorTypeResourceNotFound && w.rdsCancel != nil {
   102  			w.rdsCancel()
   103  			w.rdsName = ""
   104  			w.rdsCancel = nil
   105  			w.lastUpdate = serviceUpdate{}
   106  		}
   107  		// The other error cases still return early without canceling the
   108  		// existing RDS watch.
   109  		w.serviceCb(serviceUpdate{}, err)
   110  		return
   111  	}
   112  
   113  	w.lastUpdate.ldsConfig = ldsConfig{
   114  		maxStreamDuration: update.MaxStreamDuration,
   115  		httpFilterConfig:  update.HTTPFilters,
   116  	}
   117  
   118  	if update.InlineRouteConfig != nil {
   119  		// If there was an RDS watch, cancel it.
   120  		w.rdsName = ""
   121  		if w.rdsCancel != nil {
   122  			w.rdsCancel()
   123  			w.rdsCancel = nil
   124  		}
   125  
   126  		// Handle the inline RDS update as if it's from an RDS watch.
   127  		w.applyRouteConfigUpdate(*update.InlineRouteConfig)
   128  		return
   129  	}
   130  
   131  	// RDS name from update is not an empty string, need RDS to fetch the
   132  	// routes.
   133  
   134  	if w.rdsName == update.RouteConfigName {
   135  		// If the new RouteConfigName is same as the previous, don't cancel and
   136  		// restart the RDS watch.
   137  		//
   138  		// If the route name did change, then we must wait until the first RDS
   139  		// update before reporting this LDS config.
   140  		if w.lastUpdate.virtualHost != nil {
   141  			// We want to send an update with the new fields from the new LDS
   142  			// (e.g. max stream duration), and old fields from the the previous
   143  			// RDS.
   144  			//
   145  			// But note that this should only happen when virtual host is set,
   146  			// which means an RDS was received.
   147  			w.serviceCb(w.lastUpdate, nil)
   148  		}
   149  		return
   150  	}
   151  	w.rdsName = update.RouteConfigName
   152  	if w.rdsCancel != nil {
   153  		w.rdsCancel()
   154  	}
   155  	w.rdsCancel = w.c.WatchRouteConfig(update.RouteConfigName, w.handleRDSResp)
   156  }
   157  
   158  func (w *serviceUpdateWatcher) applyRouteConfigUpdate(update xdsresource.RouteConfigUpdate) {
   159  	matchVh := xdsresource.FindBestMatchingVirtualHost(w.serviceName, update.VirtualHosts)
   160  	if matchVh == nil {
   161  		// No matching virtual host found.
   162  		w.serviceCb(serviceUpdate{}, fmt.Errorf("no matching virtual host found for %q", w.serviceName))
   163  		return
   164  	}
   165  
   166  	w.lastUpdate.virtualHost = matchVh
   167  	w.lastUpdate.clusterSpecifierPlugins = update.ClusterSpecifierPlugins
   168  	w.serviceCb(w.lastUpdate, nil)
   169  }
   170  
   171  func (w *serviceUpdateWatcher) handleRDSResp(update xdsresource.RouteConfigUpdate, err error) {
   172  	w.logger.Infof("received RDS update: %+v, err: %v", pretty.ToJSON(update), err)
   173  	w.mu.Lock()
   174  	defer w.mu.Unlock()
   175  	if w.closed {
   176  		return
   177  	}
   178  	if w.rdsCancel == nil {
   179  		// This mean only the RDS watch is canceled, can happen if the LDS
   180  		// resource is removed.
   181  		return
   182  	}
   183  	if err != nil {
   184  		w.serviceCb(serviceUpdate{}, err)
   185  		return
   186  	}
   187  	w.applyRouteConfigUpdate(update)
   188  }
   189  
   190  func (w *serviceUpdateWatcher) close() {
   191  	w.mu.Lock()
   192  	defer w.mu.Unlock()
   193  	w.closed = true
   194  	w.ldsCancel()
   195  	if w.rdsCancel != nil {
   196  		w.rdsCancel()
   197  		w.rdsCancel = nil
   198  	}
   199  }