dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/balancer/clusterresolver/resource_resolver.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  /*
    19   *
    20   * Copyright 2021 gRPC authors.
    21   *
    22   */
    23  
    24  package clusterresolver
    25  
    26  import (
    27  	"sync"
    28  )
    29  
    30  import (
    31  	"dubbo.apache.org/dubbo-go/v3/xds/client"
    32  	"dubbo.apache.org/dubbo-go/v3/xds/client/resource"
    33  )
    34  
    35  // resourceUpdate is a combined update from all the resources, in the order of
    36  // priority. For example, it can be {EDS, EDS, DNS}.
    37  type resourceUpdate struct {
    38  	priorities []priorityConfig
    39  	err        error
    40  }
    41  
    42  type discoveryMechanism interface {
    43  	lastUpdate() (interface{}, bool)
    44  	resolveNow()
    45  	stop()
    46  }
    47  
    48  // discoveryMechanismKey is {type+resource_name}, it's used as the map key, so
    49  // that the same resource resolver can be reused (e.g. when there are two
    50  // mechanisms, both for the same EDS resource, but has different circuit
    51  // breaking config.
    52  type discoveryMechanismKey struct {
    53  	typ  DiscoveryMechanismType
    54  	name string
    55  }
    56  
    57  // resolverMechanismTuple is needed to keep the resolver and the discovery
    58  // mechanism together, because resolvers can be shared. And we need the
    59  // mechanism for fields like circuit breaking, LRS etc when generating the
    60  // balancer config.
    61  type resolverMechanismTuple struct {
    62  	dm    DiscoveryMechanism
    63  	dmKey discoveryMechanismKey
    64  	r     discoveryMechanism
    65  }
    66  
    67  type resourceResolver struct {
    68  	parent        *clusterResolverBalancer
    69  	updateChannel chan *resourceUpdate
    70  
    71  	// mu protects the slice and map, and content of the resolvers in the slice.
    72  	mu          sync.Mutex
    73  	mechanisms  []DiscoveryMechanism
    74  	children    []resolverMechanismTuple
    75  	childrenMap map[discoveryMechanismKey]discoveryMechanism
    76  }
    77  
    78  func newResourceResolver(parent *clusterResolverBalancer) *resourceResolver {
    79  	return &resourceResolver{
    80  		parent:        parent,
    81  		updateChannel: make(chan *resourceUpdate, 1),
    82  		childrenMap:   make(map[discoveryMechanismKey]discoveryMechanism),
    83  	}
    84  }
    85  
    86  func equalDiscoveryMechanisms(a, b []DiscoveryMechanism) bool {
    87  	if len(a) != len(b) {
    88  		return false
    89  	}
    90  	for i, aa := range a {
    91  		bb := b[i]
    92  		if !aa.Equal(bb) {
    93  			return false
    94  		}
    95  	}
    96  	return true
    97  }
    98  
    99  func (rr *resourceResolver) updateMechanisms(mechanisms []DiscoveryMechanism) {
   100  	rr.mu.Lock()
   101  	defer rr.mu.Unlock()
   102  	if equalDiscoveryMechanisms(rr.mechanisms, mechanisms) {
   103  		return
   104  	}
   105  	rr.mechanisms = mechanisms
   106  	rr.children = make([]resolverMechanismTuple, len(mechanisms))
   107  	newDMs := make(map[discoveryMechanismKey]bool)
   108  
   109  	// Start one watch for each new discover mechanism {type+resource_name}.
   110  	for i, dm := range mechanisms {
   111  		switch dm.Type {
   112  		case DiscoveryMechanismTypeEDS:
   113  			// If EDSServiceName is not set, use the cluster name as EDS service
   114  			// name to watch.
   115  			nameToWatch := dm.EDSServiceName
   116  			if nameToWatch == "" {
   117  				nameToWatch = dm.Cluster
   118  			}
   119  			dmKey := discoveryMechanismKey{typ: dm.Type, name: nameToWatch}
   120  			newDMs[dmKey] = true
   121  
   122  			r := rr.childrenMap[dmKey]
   123  			if r == nil {
   124  				r = newEDSResolver(nameToWatch, rr.parent.xdsClient, rr)
   125  				rr.childrenMap[dmKey] = r
   126  			}
   127  			rr.children[i] = resolverMechanismTuple{dm: dm, dmKey: dmKey, r: r}
   128  		case DiscoveryMechanismTypeLogicalDNS:
   129  			// Name to resolve in DNS is the hostname, not the ClientConn
   130  			// target.
   131  			dmKey := discoveryMechanismKey{typ: dm.Type, name: dm.DNSHostname}
   132  			newDMs[dmKey] = true
   133  
   134  			r := rr.childrenMap[dmKey]
   135  			if r == nil {
   136  				r = newDNSResolver(dm.DNSHostname, rr)
   137  				rr.childrenMap[dmKey] = r
   138  			}
   139  			rr.children[i] = resolverMechanismTuple{dm: dm, dmKey: dmKey, r: r}
   140  		}
   141  	}
   142  	// Stop the resources that were removed.
   143  	for dm, r := range rr.childrenMap {
   144  		if !newDMs[dm] {
   145  			delete(rr.childrenMap, dm)
   146  			r.stop()
   147  		}
   148  	}
   149  	// Regenerate even if there's no change in discovery mechanism, in case
   150  	// priority order changed.
   151  	rr.generate()
   152  }
   153  
   154  // resolveNow is typically called to trigger re-resolve of DNS. The EDS
   155  // resolveNow() is a noop.
   156  func (rr *resourceResolver) resolveNow() {
   157  	rr.mu.Lock()
   158  	defer rr.mu.Unlock()
   159  	for _, r := range rr.childrenMap {
   160  		r.resolveNow()
   161  	}
   162  }
   163  
   164  func (rr *resourceResolver) stop() {
   165  	rr.mu.Lock()
   166  	defer rr.mu.Unlock()
   167  	for dm, r := range rr.childrenMap {
   168  		delete(rr.childrenMap, dm)
   169  		r.stop()
   170  	}
   171  	rr.mechanisms = nil
   172  	rr.children = nil
   173  }
   174  
   175  // generate collects all the updates from all the resolvers, and push the
   176  // combined result into the update channel. It only pushes the update when all
   177  // the child resolvers have received at least one update, otherwise it will
   178  // wait.
   179  //
   180  // caller must hold rr.mu.
   181  func (rr *resourceResolver) generate() {
   182  	var ret []priorityConfig
   183  	for _, rDM := range rr.children {
   184  		r, ok := rr.childrenMap[rDM.dmKey]
   185  		if !ok {
   186  			rr.parent.logger.Infof("resolver for %+v not found, should never happen", rDM.dmKey)
   187  			continue
   188  		}
   189  
   190  		u, ok := r.lastUpdate()
   191  		if !ok {
   192  			// Don't send updates to parent until all resolvers have update to
   193  			// send.
   194  			return
   195  		}
   196  		switch uu := u.(type) {
   197  		case resource.EndpointsUpdate:
   198  			ret = append(ret, priorityConfig{mechanism: rDM.dm, edsResp: uu})
   199  		case []string:
   200  			ret = append(ret, priorityConfig{mechanism: rDM.dm, addresses: uu})
   201  		}
   202  	}
   203  	select {
   204  	case <-rr.updateChannel:
   205  	default:
   206  	}
   207  	rr.updateChannel <- &resourceUpdate{priorities: ret}
   208  }
   209  
   210  type edsDiscoveryMechanism struct {
   211  	cancel func()
   212  
   213  	update         resource.EndpointsUpdate
   214  	updateReceived bool
   215  }
   216  
   217  func (er *edsDiscoveryMechanism) lastUpdate() (interface{}, bool) {
   218  	if !er.updateReceived {
   219  		return nil, false
   220  	}
   221  	return er.update, true
   222  }
   223  
   224  func (er *edsDiscoveryMechanism) resolveNow() {
   225  }
   226  
   227  func (er *edsDiscoveryMechanism) stop() {
   228  	er.cancel()
   229  }
   230  
   231  // newEDSResolver starts the EDS watch on the given xds client.
   232  func newEDSResolver(nameToWatch string, xdsc client.XDSClient, topLevelResolver *resourceResolver) *edsDiscoveryMechanism {
   233  	ret := &edsDiscoveryMechanism{}
   234  	topLevelResolver.parent.logger.Infof("EDS watch started on %v", nameToWatch)
   235  	cancel := xdsc.WatchEndpoints(nameToWatch, func(update resource.EndpointsUpdate, err error) {
   236  		topLevelResolver.mu.Lock()
   237  		defer topLevelResolver.mu.Unlock()
   238  		if err != nil {
   239  			select {
   240  			case <-topLevelResolver.updateChannel:
   241  			default:
   242  			}
   243  			topLevelResolver.updateChannel <- &resourceUpdate{err: err}
   244  			return
   245  		}
   246  		ret.update = update
   247  		ret.updateReceived = true
   248  		topLevelResolver.generate()
   249  	})
   250  	ret.cancel = func() {
   251  		topLevelResolver.parent.logger.Infof("EDS watch canceled on %v", nameToWatch)
   252  		cancel()
   253  	}
   254  	return ret
   255  }