dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/xds/client.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  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  package xds
    19  
    20  import (
    21  	"errors"
    22  	"sync"
    23  	"time"
    24  )
    25  
    26  import (
    27  	"github.com/dubbogo/gost/log/logger"
    28  
    29  	perrors "github.com/pkg/errors"
    30  )
    31  
    32  import (
    33  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    34  	"dubbo.apache.org/dubbo-go/v3/protocol"
    35  	"dubbo.apache.org/dubbo-go/v3/registry"
    36  	xdsCommon "dubbo.apache.org/dubbo-go/v3/remoting/xds/common"
    37  	"dubbo.apache.org/dubbo-go/v3/remoting/xds/ewatcher"
    38  	"dubbo.apache.org/dubbo-go/v3/remoting/xds/mapping"
    39  	"dubbo.apache.org/dubbo-go/v3/xds/client"
    40  	"dubbo.apache.org/dubbo-go/v3/xds/client/resource"
    41  	"dubbo.apache.org/dubbo-go/v3/xds/utils/resolver"
    42  )
    43  
    44  const (
    45  	// todo make istiodTokenPath configurable
    46  	defaultIstiodTokenPath          = "/var/run/secrets/token/istio-token"
    47  	defaultIstiodDebugPort          = "8080"
    48  	gRPCUserAgentName               = "gRPC Go"
    49  	clientFeatureNoOverprovisioning = "envoy.lb.does_not_support_overprovisioning"
    50  )
    51  
    52  // xdsWrappedClient should only init once
    53  var xdsWrappedClient *WrappedClientImpl
    54  
    55  type WrappedClientImpl struct {
    56  	/*
    57  		local info
    58  	*/
    59  	podName   string
    60  	namespace string
    61  
    62  	/*
    63  		localIP is  to find local pod's cluster and hostAddr by cds and eds
    64  	*/
    65  	localIP string
    66  
    67  	/*
    68  		hostAddr is local pod's cluster and hostAddr, like dubbo-go-app.default.svc.cluster.local:20000
    69  	*/
    70  	hostAddr xdsCommon.HostAddr
    71  
    72  	/*
    73  		istiod info
    74  		istiodAddr is istio $(istioSeviceFullName):$(xds-grpc-port) like istiod.istio-system.svc.cluster.local:15010
    75  		istiodPodIP is to call istiod unexposed debug port 8080
    76  	*/
    77  	istiodAddr  xdsCommon.HostAddr
    78  	istiodPodIP string
    79  
    80  	/*
    81  		grpc xdsClient sdk
    82  	*/
    83  	xdsClient client.XDSClient
    84  
    85  	/*
    86  		interfaceMapHandler manages dubbogo metadata containing service key -> hostAddr map
    87  	*/
    88  	interfaceMapHandler mapping.InterfaceMapHandler
    89  
    90  	/*
    91  		rdsMap cache router config
    92  		mesh router would read config from it
    93  	*/
    94  	rdsMap     map[string]resource.RouteConfigUpdate
    95  	rdsMapLock sync.RWMutex
    96  
    97  	/*
    98  		cdsMap cache full clusterID -> clusterUpdate map of this istiod
    99  	*/
   100  	cdsMap     map[string]resource.ClusterUpdate
   101  	cdsMapLock sync.RWMutex
   102  
   103  	/*
   104  		cdsUpdateEventChan transfer cds update event from xdsClient
   105  		if update event got, we will refresh cds watcher, stopping endPointWatcherCtx related to deleted cluster, and starting
   106  		to watch new-coming cluster with endPointWatcherCtx
   107  
   108  		cdsUpdateEventHandlers stores handlers to recv refresh event, refresh event is only a call without param,
   109  		after the calling event, we can read cdsMap to get latest and full cluster info, and handle the difference.
   110  	*/
   111  	cdsUpdateEventChan         chan struct{}
   112  	cdsUpdateEventHandlers     []func()
   113  	cdsUpdateEventHandlersLock sync.RWMutex
   114  
   115  	/*
   116  		hostAddrListenerMap[hostAddr][serviceUniqueKey] -> registry.NotifyListener
   117  		stores all directory listener, which receives events and refresh invokers
   118  	*/
   119  	hostAddrListenerMap     map[string]map[string]registry.NotifyListener
   120  	hostAddrListenerMapLock sync.RWMutex
   121  
   122  	/*
   123  		hostAddrClusterCtxMap[hostAddr][clusterName] -> endPointWatcherCtx
   124  	*/
   125  	hostAddrClusterCtxMap     map[string]map[string]ewatcher.EWatcher
   126  	hostAddrClusterCtxMapLock sync.RWMutex
   127  
   128  	/*
   129  		subscribeStopChMap stores subscription stop chan
   130  	*/
   131  	subscribeStopChMap sync.Map
   132  
   133  	/*
   134  		xdsSniffingTimeout stores xds sniffing timeout duration
   135  	*/
   136  	xdsSniffingTimeout time.Duration
   137  }
   138  
   139  func GetXDSWrappedClient() *WrappedClientImpl {
   140  	return xdsWrappedClient
   141  }
   142  
   143  // NewXDSWrappedClient create or get singleton xdsWrappedClient
   144  func NewXDSWrappedClient(config Config) (XDSWrapperClient, error) {
   145  	// todo @(laurence) safety problem? what if to concurrent 'new' both create new client?
   146  	if xdsWrappedClient != nil {
   147  		return xdsWrappedClient, nil
   148  	}
   149  	if config.SniffingTimeout == 0 {
   150  		config.SniffingTimeout, _ = time.ParseDuration(constant.DefaultRegTimeout)
   151  	}
   152  	if config.DebugPort == "" {
   153  		config.DebugPort = "8080"
   154  	}
   155  
   156  	// write param
   157  	newClient := &WrappedClientImpl{
   158  		podName:    config.PodName,
   159  		namespace:  config.Namespace,
   160  		localIP:    config.LocalIP,
   161  		istiodAddr: config.IstioAddr,
   162  
   163  		rdsMap: make(map[string]resource.RouteConfigUpdate),
   164  		cdsMap: make(map[string]resource.ClusterUpdate),
   165  
   166  		hostAddrListenerMap:   make(map[string]map[string]registry.NotifyListener),
   167  		hostAddrClusterCtxMap: make(map[string]map[string]ewatcher.EWatcher),
   168  
   169  		cdsUpdateEventChan:     make(chan struct{}),
   170  		cdsUpdateEventHandlers: make([]func(), 0),
   171  
   172  		xdsSniffingTimeout: config.SniffingTimeout,
   173  	}
   174  
   175  	// 1. init xdsclient
   176  	if err := newClient.initXDSClient(); err != nil {
   177  		return nil, err
   178  	}
   179  	// 2. watching cds update event
   180  	// todo @(laurence)  gr control
   181  	go newClient.runWatchingCdsUpdateEvent()
   182  
   183  	// 3. load basic info from istiod and start listening cds
   184  	if err := newClient.startWatchingAllClusterAndLoadLocalHostAddrAndIstioPodIP(config.LocalDebugMode); err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	// 4. init interface map handler
   189  	newClient.interfaceMapHandler = mapping.NewInterfaceMapHandlerImpl(
   190  		newClient.xdsClient,
   191  		defaultIstiodTokenPath,
   192  		xdsCommon.NewHostNameOrIPAddr(newClient.istiodPodIP+":"+config.DebugPort),
   193  		newClient.hostAddr, config.LocalDebugMode)
   194  
   195  	xdsWrappedClient = newClient
   196  	return newClient, nil
   197  }
   198  
   199  // GetHostAddrByServiceUniqueKey  todo 1. timeout 2. hostAddr change?
   200  func (w *WrappedClientImpl) GetHostAddrByServiceUniqueKey(serviceUniqueKey string) (string, error) {
   201  	return w.interfaceMapHandler.GetHostAddrMap(serviceUniqueKey)
   202  }
   203  
   204  // GetDubboGoMetadata get all registered metadata of dubbogo
   205  func (w *WrappedClientImpl) GetDubboGoMetadata() (map[string]string, error) {
   206  	return w.interfaceMapHandler.GetDubboGoMetadata()
   207  }
   208  
   209  // ChangeInterfaceMap change the map of serviceUniqueKey -> appname, if add is true, register, else unregister
   210  func (w *WrappedClientImpl) ChangeInterfaceMap(serviceUniqueKey string, add bool) error {
   211  	if add {
   212  		return w.interfaceMapHandler.Register(serviceUniqueKey)
   213  	}
   214  	return w.interfaceMapHandler.UnRegister(serviceUniqueKey)
   215  }
   216  
   217  func (w *WrappedClientImpl) GetRouterConfig(hostAddr string) resource.RouteConfigUpdate {
   218  	w.rdsMapLock.RLock()
   219  	defer w.rdsMapLock.RUnlock()
   220  	routeConfig, ok := w.rdsMap[hostAddr]
   221  	if ok {
   222  		return routeConfig
   223  	}
   224  	return resource.RouteConfigUpdate{}
   225  }
   226  
   227  func (w *WrappedClientImpl) GetClusterUpdateIgnoreVersion(hostAddr string) resource.ClusterUpdate {
   228  	addr := xdsCommon.NewHostNameOrIPAddr(hostAddr)
   229  	w.cdsMapLock.RLock()
   230  	defer w.cdsMapLock.Unlock()
   231  	for clusterName, v := range w.cdsMap {
   232  		cluster := xdsCommon.NewCluster(clusterName)
   233  		if cluster.Addr.Port == addr.Port && cluster.Addr.HostnameOrIP == addr.HostnameOrIP {
   234  			return v
   235  		}
   236  	}
   237  	return resource.ClusterUpdate{}
   238  }
   239  
   240  func (w *WrappedClientImpl) Subscribe(svcUniqueName, interfaceName, hostAddr string, lst registry.NotifyListener) error {
   241  	_, ok := w.subscribeStopChMap.Load(svcUniqueName)
   242  	if ok {
   243  		return perrors.Errorf("XDS WrappedClientImpl subscribe interface %s failed, subscription already exist.", interfaceName)
   244  	}
   245  	stopCh := make(chan struct{})
   246  	w.subscribeStopChMap.Store(svcUniqueName, stopCh)
   247  	w.registerHostLevelSubscription(hostAddr, interfaceName, svcUniqueName, lst)
   248  	<-stopCh
   249  	w.unregisterHostLevelSubscription(hostAddr, svcUniqueName)
   250  	return nil
   251  }
   252  
   253  func (w *WrappedClientImpl) UnSubscribe(svcUniqueName string) {
   254  	if stopCh, ok := w.subscribeStopChMap.Load(svcUniqueName); ok {
   255  		close(stopCh.(chan struct{}))
   256  	}
   257  	w.subscribeStopChMap.Delete(svcUniqueName)
   258  }
   259  
   260  func (w *WrappedClientImpl) GetHostAddress() xdsCommon.HostAddr {
   261  	return w.hostAddr
   262  }
   263  
   264  func (w *WrappedClientImpl) GetIstioPodIP() string {
   265  	return w.istiodPodIP
   266  }
   267  
   268  // registerHostLevelSubscription register: 1. all related cluster, 2. router config
   269  func (w *WrappedClientImpl) registerHostLevelSubscription(hostAddr, interfaceName, svcUniqueName string, lst registry.NotifyListener) {
   270  	// 1. listen all cluster related endpoint
   271  	w.hostAddrListenerMapLock.Lock()
   272  	if _, ok := w.hostAddrListenerMap[hostAddr]; ok {
   273  		// if subscription exist, register listener directly
   274  		w.hostAddrListenerMap[hostAddr][svcUniqueName] = lst
   275  		w.hostAddrListenerMapLock.Unlock()
   276  		return
   277  	}
   278  	// host HostAddr key must not exist in map, create one
   279  	w.hostAddrListenerMap[hostAddr] = make(map[string]registry.NotifyListener)
   280  
   281  	w.hostAddrClusterCtxMapLock.Lock()
   282  	w.hostAddrClusterCtxMap[hostAddr] = make(map[string]ewatcher.EWatcher)
   283  	w.hostAddrClusterCtxMapLock.Unlock()
   284  
   285  	w.hostAddrListenerMap[hostAddr][svcUniqueName] = lst
   286  	w.hostAddrListenerMapLock.Unlock()
   287  
   288  	// watch cluster change, and start listening newcoming cluster
   289  	w.cdsUpdateEventHandlersLock.Lock()
   290  	w.cdsUpdateEventHandlers = append(w.cdsUpdateEventHandlers, func() {
   291  		// todo @(laurnece) now this event would be called if any cluster is change, but not only this hostAddr's
   292  		updatedAllVersionedClusterName := w.getAllVersionClusterName(hostAddr)
   293  		// do patch
   294  		w.hostAddrClusterCtxMapLock.RLock()
   295  		listeningClustersCancelMap := w.hostAddrClusterCtxMap[hostAddr]
   296  		w.hostAddrClusterCtxMapLock.RUnlock()
   297  
   298  		oldlisteningClusterMap := make(map[string]bool)
   299  		for cluster := range listeningClustersCancelMap {
   300  			oldlisteningClusterMap[cluster] = false
   301  		}
   302  		for _, updatedClusterName := range updatedAllVersionedClusterName {
   303  			if _, ok := listeningClustersCancelMap[updatedClusterName]; ok {
   304  				// already listening
   305  				oldlisteningClusterMap[updatedClusterName] = true
   306  				continue
   307  			}
   308  			// new cluster
   309  			watcher := ewatcher.NewEndpointWatcherCtxImpl(
   310  				updatedClusterName, hostAddr, interfaceName, &w.hostAddrListenerMapLock, w.hostAddrListenerMap)
   311  			cancel := w.xdsClient.WatchEndpoints(updatedClusterName, watcher.Handle)
   312  			watcher.SetCancelFunction(cancel)
   313  			w.hostAddrClusterCtxMapLock.Lock()
   314  			w.hostAddrClusterCtxMap[hostAddr][updatedClusterName] = watcher
   315  			w.hostAddrClusterCtxMapLock.Unlock()
   316  		}
   317  
   318  		// cancel not exist cluster
   319  		for cluster, v := range oldlisteningClusterMap {
   320  			if !v {
   321  				// this cluster not exist in update cluster list
   322  				w.hostAddrClusterCtxMapLock.Lock()
   323  				if watchCtx, ok := w.hostAddrClusterCtxMap[hostAddr][cluster]; ok {
   324  					delete(w.hostAddrClusterCtxMap[hostAddr], cluster)
   325  					watchCtx.Destroy()
   326  				}
   327  				w.hostAddrClusterCtxMapLock.Unlock()
   328  			}
   329  		}
   330  	})
   331  	w.cdsUpdateEventHandlersLock.Unlock()
   332  
   333  	// update cluster of now
   334  	allVersionedClusterName := w.getAllVersionClusterName(hostAddr)
   335  	for _, c := range allVersionedClusterName {
   336  		watcher := ewatcher.NewEndpointWatcherCtxImpl(
   337  			c, hostAddr, interfaceName, &w.hostAddrListenerMapLock, w.hostAddrListenerMap)
   338  		watcher.SetCancelFunction(w.xdsClient.WatchEndpoints(c, watcher.Handle))
   339  
   340  		w.hostAddrClusterCtxMapLock.Lock()
   341  		w.hostAddrClusterCtxMap[hostAddr][c] = watcher
   342  		w.hostAddrClusterCtxMapLock.Unlock()
   343  	}
   344  
   345  	// 2. cache route config
   346  	// todo @(laurnece) cancel watching of this addr's rds
   347  	_ = w.xdsClient.WatchRouteConfig(hostAddr, func(update resource.RouteConfigUpdate, err error) {
   348  		if update.VirtualHosts == nil {
   349  			return
   350  		}
   351  		w.rdsMapLock.Lock()
   352  		defer w.rdsMapLock.Unlock()
   353  		w.rdsMap[hostAddr] = update
   354  	})
   355  }
   356  
   357  func (w *WrappedClientImpl) unregisterHostLevelSubscription(hostAddr, svcUniqueName string) {
   358  	w.hostAddrListenerMapLock.Lock()
   359  	defer w.hostAddrListenerMapLock.Unlock()
   360  	if _, ok := w.hostAddrListenerMap[hostAddr]; ok {
   361  		// if subscription exist, register listener directly
   362  		if _, exist := w.hostAddrListenerMap[hostAddr][svcUniqueName]; exist {
   363  			delete(w.hostAddrListenerMap[hostAddr], svcUniqueName)
   364  		}
   365  		if (len(w.hostAddrListenerMap[hostAddr])) == 0 {
   366  			// if no subscription of this host cancel all cds subscription of this hostAddr
   367  			keys := make([]string, 0)
   368  			w.hostAddrClusterCtxMapLock.Lock()
   369  			for k, c := range w.hostAddrClusterCtxMap[hostAddr] {
   370  				c.Destroy()
   371  				keys = append(keys, k)
   372  			}
   373  			for _, v := range keys {
   374  				delete(w.hostAddrClusterCtxMap, v)
   375  			}
   376  			w.hostAddrClusterCtxMapLock.Unlock()
   377  		}
   378  	}
   379  }
   380  
   381  func (w *WrappedClientImpl) initXDSClient() error {
   382  	xdsClient, err := xdsClientFactoryFunction(w.localIP, w.podName, w.namespace, w.istiodAddr)
   383  	if err != nil {
   384  		return err
   385  	}
   386  	w.xdsClient = xdsClient
   387  	return nil
   388  }
   389  
   390  // startWatchingAllClusterAndLoadLocalHostAddrAndIstioPodIP is blocking function
   391  // 1. start watching all cluster by cds
   392  // 2. discovery local pod's hostAddr by cds and eds
   393  // 3. discovery istiod pod ip by cds and eds
   394  func (w *WrappedClientImpl) startWatchingAllClusterAndLoadLocalHostAddrAndIstioPodIP(localDebugMode bool) error {
   395  	// call watch and refresh istiod debug interface
   396  	foundLocalStopCh := make(chan struct{})
   397  	foundIstiodStopCh := make(chan struct{})
   398  	discoveryFinishedStopCh := make(chan struct{})
   399  	// todo timeout configure
   400  	timeoutCh := time.After(w.xdsSniffingTimeout)
   401  	foundLocal := false
   402  	foundIstiod := false
   403  	var cancel1 func()
   404  	var cancel2 func()
   405  	logger.Infof("[XDS Wrapped Client] Start sniffing with istio hostname = %s, localIp = %s",
   406  		w.istiodAddr.HostnameOrIP, w.localIP)
   407  
   408  	// todo @(laurence) here, if istiod is unhealthy, here should be timeout and tell developer.
   409  	_ = w.xdsClient.WatchCluster("*", func(update resource.ClusterUpdate, err error) {
   410  		if update.ClusterName == "" {
   411  			return
   412  		}
   413  		if update.ClusterName[:1] == constant.MeshDeleteClusterPrefix {
   414  			// delete event
   415  			w.cdsMapLock.Lock()
   416  			defer w.cdsMapLock.Unlock()
   417  			delete(w.cdsMap, update.ClusterName[1:])
   418  			logger.Infof("[XDS Wrapped Client] Delete cluster %s", update.ClusterName)
   419  			w.cdsUpdateEventChan <- struct{}{} // send update event
   420  			return
   421  		}
   422  		w.cdsMapLock.Lock()
   423  		w.cdsMap[update.ClusterName] = update
   424  		w.cdsMapLock.Unlock()
   425  
   426  		w.cdsUpdateEventChan <- struct{}{} // send update event
   427  		if foundLocal && foundIstiod {
   428  			return
   429  		}
   430  		logger.Infof("[XDS Wrapped Client] Sniffing with cluster name = %s", update.ClusterName)
   431  		// only into here during start sniffing istiod/service prcedure
   432  		cluster := xdsCommon.NewCluster(update.ClusterName)
   433  		if cluster.Addr.HostnameOrIP == w.istiodAddr.HostnameOrIP {
   434  			// 1. find istiod podIP
   435  			// todo: When would eds level watch be canceled?
   436  			logger.Info("[XDS Wrapped Client] Sniffing get istiod cluster")
   437  			cancel1 = w.xdsClient.WatchEndpoints(update.ClusterName, func(endpoint resource.EndpointsUpdate, err error) {
   438  				if foundIstiod {
   439  					return
   440  				}
   441  				logger.Infof("[XDS Wrapped Client] Sniffing get istiod endpoint = %+v, localities = %+v", endpoint, endpoint.Localities)
   442  				for _, v := range endpoint.Localities {
   443  					for _, e := range v.Endpoints {
   444  						w.istiodPodIP = xdsCommon.NewHostNameOrIPAddr(e.Address).HostnameOrIP
   445  						logger.Infof("[XDS Wrapped Client] Sniffing found istiod podIP = %s", w.istiodPodIP)
   446  						foundIstiod = true
   447  						close(foundIstiodStopCh)
   448  					}
   449  				}
   450  			})
   451  			return
   452  		}
   453  		// 2. found local hostAddr
   454  		// todo: When would eds level watch be canceled?
   455  		cancel2 = w.xdsClient.WatchEndpoints(update.ClusterName, func(endpoint resource.EndpointsUpdate, err error) {
   456  			if foundLocal {
   457  				return
   458  			}
   459  			for _, v := range endpoint.Localities {
   460  				for _, e := range v.Endpoints {
   461  					logger.Infof("[XDS Wrapped Client] Sniffing Found eds endpoint = %+v", e)
   462  					if xdsCommon.NewHostNameOrIPAddr(e.Address).HostnameOrIP == w.localIP {
   463  						cluster := xdsCommon.NewCluster(update.ClusterName)
   464  						w.hostAddr = cluster.Addr
   465  						foundLocal = true
   466  						close(foundLocalStopCh)
   467  					}
   468  				}
   469  			}
   470  		})
   471  	})
   472  
   473  	if localDebugMode {
   474  		go func() {
   475  			<-foundIstiodStopCh
   476  			<-foundLocalStopCh
   477  			cancel1()
   478  			cancel2()
   479  		}()
   480  		return nil
   481  	}
   482  
   483  	go func() {
   484  		<-foundIstiodStopCh
   485  		<-foundLocalStopCh
   486  		close(discoveryFinishedStopCh)
   487  	}()
   488  
   489  	select {
   490  	case <-discoveryFinishedStopCh:
   491  		// discovery success
   492  		// waiting for cancel function to have value
   493  		time.Sleep(time.Second)
   494  		cancel1()
   495  		cancel2()
   496  		logger.Infof("[XDS Wrapper Client] Sniffing Finished with host addr = %s, istiod pod ip = %s", w.hostAddr, w.istiodPodIP)
   497  		return nil
   498  	case <-timeoutCh:
   499  		logger.Warnf("[XDS Wrapper Client] Sniffing timeout with duration = %v", w.xdsSniffingTimeout)
   500  		if cancel1 != nil {
   501  			cancel1()
   502  		}
   503  		if cancel2 != nil {
   504  			cancel2()
   505  		}
   506  		select {
   507  		case <-foundIstiodStopCh:
   508  			return DiscoverLocalError
   509  		default:
   510  			return DiscoverIstiodPodIpError
   511  		}
   512  	}
   513  }
   514  
   515  // runWatchingCdsUpdateEvent is blocking function, starts to read event from cdsUpdateEventChan and call cdsUpdateEventHandlers
   516  func (w *WrappedClientImpl) runWatchingCdsUpdateEvent() {
   517  	for range w.cdsUpdateEventChan {
   518  		w.cdsUpdateEventHandlersLock.RLock()
   519  		for _, h := range w.cdsUpdateEventHandlers {
   520  			h()
   521  		}
   522  		w.cdsUpdateEventHandlersLock.RUnlock()
   523  	}
   524  }
   525  
   526  // getAllVersionClusterName get all clusterID that is the subset of given hostAddr from cache: cdsMap
   527  // like: if given hostAddr is 'outbound|20000||dubbo-go-app.default.svc.cluster.local', and result would be
   528  // ['outbound|20000|v1|dubbo-go-app.default.svc.cluster.local',
   529  // 'outbound|20000||dubbo-go-app.default.svc.cluster.local',
   530  // 'outbound|20000|v2|dubbo-go-app.default.svc.cluster.local']
   531  func (w *WrappedClientImpl) getAllVersionClusterName(hostAddr string) []string {
   532  	addr := xdsCommon.NewHostNameOrIPAddr(hostAddr)
   533  	allVersionClusterNames := make([]string, 0)
   534  	w.cdsMapLock.RLock()
   535  	defer w.cdsMapLock.RUnlock()
   536  	for clusterName, _ := range w.cdsMap {
   537  		cluster := xdsCommon.NewCluster(clusterName)
   538  		if cluster.Addr.Port == addr.Port && cluster.Addr.HostnameOrIP == addr.HostnameOrIP {
   539  			allVersionClusterNames = append(allVersionClusterNames, clusterName)
   540  		}
   541  	}
   542  	return allVersionClusterNames
   543  }
   544  
   545  func (w *WrappedClientImpl) MatchRoute(routerConfig resource.RouteConfigUpdate, invocation protocol.Invocation) (*resource.Route, error) {
   546  	ctx := invocation.GetAttachmentAsContext()
   547  	rpcInfo := resolver.RPCInfo{
   548  		Context: ctx,
   549  		Method:  "/" + invocation.MethodName(),
   550  	}
   551  	// try to route to sub virtual host
   552  	for _, vh := range routerConfig.VirtualHosts {
   553  		for _, r := range vh.Routes {
   554  			//route.
   555  			matcher, err := resource.RouteToMatcher(r)
   556  			if err != nil {
   557  				return nil, err
   558  			}
   559  			if matcher.Match(rpcInfo) {
   560  				return r, nil
   561  			}
   562  		}
   563  	}
   564  	return nil, errors.New("not found route")
   565  }
   566  
   567  type XDSWrapperClient interface {
   568  	Subscribe(svcUniqueName, interfaceName, hostAddr string, lst registry.NotifyListener) error
   569  	UnSubscribe(svcUniqueName string)
   570  	GetRouterConfig(hostAddr string) resource.RouteConfigUpdate
   571  	GetHostAddrByServiceUniqueKey(serviceUniqueKey string) (string, error)
   572  	GetDubboGoMetadata() (map[string]string, error)
   573  	ChangeInterfaceMap(serviceUniqueKey string, add bool) error
   574  	GetClusterUpdateIgnoreVersion(hostAddr string) resource.ClusterUpdate
   575  	GetHostAddress() xdsCommon.HostAddr
   576  	GetIstioPodIP() string
   577  	MatchRoute(routerConfig resource.RouteConfigUpdate, invocation protocol.Invocation) (*resource.Route, error)
   578  }