dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/authority.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 client
    25  
    26  import (
    27  	"errors"
    28  	"fmt"
    29  )
    30  
    31  import (
    32  	dubbogoLogger "github.com/dubbogo/gost/log/logger"
    33  
    34  	_struct "github.com/golang/protobuf/ptypes/struct"
    35  )
    36  
    37  import (
    38  	"dubbo.apache.org/dubbo-go/v3/xds/client/bootstrap"
    39  	"dubbo.apache.org/dubbo-go/v3/xds/client/load"
    40  	"dubbo.apache.org/dubbo-go/v3/xds/client/pubsub"
    41  	"dubbo.apache.org/dubbo-go/v3/xds/client/resource"
    42  )
    43  
    44  const federationScheme = "xdstp"
    45  
    46  // findAuthority returns the authority for this name. If it doesn't already
    47  // exist, one will be created.
    48  //
    49  // Note that this doesn't always create new authority. authorities with the same
    50  // config but different names are shared.
    51  //
    52  // The returned unref function must be called when the caller is done using this
    53  // authority, without holding c.authorityMu.
    54  //
    55  // Caller must not hold c.authorityMu.
    56  func (c *clientImpl) findAuthority(n *resource.Name) (_ *authority, unref func(), _ error) {
    57  	scheme, authority := n.Scheme, n.Authority
    58  
    59  	c.authorityMu.Lock()
    60  	defer c.authorityMu.Unlock()
    61  	if c.done.HasFired() {
    62  		return nil, nil, errors.New("the xds-client is closed")
    63  	}
    64  
    65  	config := c.config.XDSServer
    66  	if scheme == federationScheme {
    67  		cfg, ok := c.config.Authorities[authority]
    68  		if !ok {
    69  			return nil, nil, fmt.Errorf("xds: failed to find authority %q", authority)
    70  		}
    71  		config = cfg.XDSServer
    72  	}
    73  
    74  	a, err := c.newAuthority(config)
    75  	if err != nil {
    76  		dubbogoLogger.Errorf(`[XDS Authority] new authority failed with error = %s, please makesure you have imported 
    77  	_ "dubbo.apache.org/dubbo-go/v3/xds/client/controller/version/v2"
    78  	_ "dubbo.apache.org/dubbo-go/v3/xds/client/controller/version/v3"`, err)
    79  		return nil, nil, fmt.Errorf("xds: failed to connect to the control plane for authority %q: %v", authority, err)
    80  	}
    81  	// All returned authority from this function will be used by a watch,
    82  	// holding the ref here.
    83  	//
    84  	// Note that this must be done while c.authorityMu is held, to avoid the
    85  	// race that an authority is returned, but before the watch starts, the
    86  	// old last watch is canceled (in another goroutine), causing this
    87  	// authority to be removed, and then a watch will start on a removed
    88  	// authority.
    89  	//
    90  	// unref() will be done when the watch is canceled.
    91  	a.ref()
    92  	return a, func() { c.unrefAuthority(a) }, nil
    93  }
    94  
    95  // newAuthority creates a new authority for the config. But before that, it
    96  // checks the cache to see if an authority for this config already exists.
    97  //
    98  // caller must hold c.authorityMu
    99  func (c *clientImpl) newAuthority(config *bootstrap.ServerConfig) (_ *authority, retErr error) {
   100  	// First check if there's already an authority for this config. If found, it
   101  	// means this authority is used by other watches (could be the same
   102  	// authority name, or a different authority name but the same server
   103  	// config). Return it.
   104  	configStr := config.String()
   105  	if a, ok := c.authorities[configStr]; ok {
   106  		return a, nil
   107  	}
   108  	// Second check if there's an authority in the idle cache. If found, it
   109  	// means this authority was created, but moved to the idle cache because the
   110  	// watch was canceled. Move it from idle cache to the authority cache, and
   111  	// return.
   112  	if old, ok := c.idleAuthorities.Remove(configStr); ok {
   113  		oldA, _ := old.(*authority)
   114  		if oldA != nil {
   115  			c.authorities[configStr] = oldA
   116  			return oldA, nil
   117  		}
   118  	}
   119  
   120  	// Make a new authority since there's no existing authority for this config.
   121  	ret := &authority{config: config, pubsub: pubsub.New(c.watchExpiryTimeout, c.logger)}
   122  	defer func() {
   123  		if retErr != nil {
   124  			ret.close()
   125  		}
   126  	}()
   127  	ctr, err := newController(config, ret.pubsub, c.updateValidator, c.logger)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	ret.controller = ctr
   132  	// Add it to the cache, so it will be reused.
   133  	c.authorities[configStr] = ret
   134  	return ret, nil
   135  }
   136  
   137  // unrefAuthority unrefs the authority. It also moves the authority to idle
   138  // cache if it's ref count is 0.
   139  //
   140  // This function doesn't need to called explicitly. It's called by the returned
   141  // unref from findAuthority().
   142  //
   143  // Caller must not hold c.authorityMu.
   144  func (c *clientImpl) unrefAuthority(a *authority) {
   145  	c.authorityMu.Lock()
   146  	defer c.authorityMu.Unlock()
   147  	if a.unref() > 0 {
   148  		return
   149  	}
   150  	configStr := a.config.String()
   151  	delete(c.authorities, configStr)
   152  	c.idleAuthorities.Add(configStr, a, func() {
   153  		a.close()
   154  	})
   155  }
   156  
   157  // authority is a combination of pubsub and the controller for this authority.
   158  //
   159  // Note that it might make sense to use one pubsub for all the resources (for
   160  // all the controllers). One downside is the handling of StoW APIs (LDS/CDS).
   161  // These responses contain all the resources from that control plane, so pubsub
   162  // will need to keep lists of resources from each control plane, to know what
   163  // are removed.
   164  type authority struct {
   165  	config     *bootstrap.ServerConfig
   166  	pubsub     *pubsub.Pubsub
   167  	controller controllerInterface
   168  	refCount   int
   169  }
   170  
   171  func (a *authority) SetMetadata(m *_struct.Struct) error {
   172  	return a.controller.SetMetadata(m)
   173  }
   174  
   175  // caller must hold parent's authorityMu.
   176  func (a *authority) ref() {
   177  	a.refCount++
   178  }
   179  
   180  // caller must hold parent's authorityMu.
   181  func (a *authority) unref() int {
   182  	a.refCount--
   183  	return a.refCount
   184  }
   185  
   186  func (a *authority) close() {
   187  	if a.pubsub != nil {
   188  		a.pubsub.Close()
   189  	}
   190  	if a.controller != nil {
   191  		a.controller.Close()
   192  	}
   193  }
   194  
   195  func (a *authority) watchListener(serviceName string, cb func(resource.ListenerUpdate, error)) (cancel func()) {
   196  	first, cancelF := a.pubsub.WatchListener(serviceName, cb)
   197  	if first {
   198  		a.controller.AddWatch(resource.ListenerResource, serviceName)
   199  	}
   200  	return func() {
   201  		if cancelF() {
   202  			a.controller.RemoveWatch(resource.ListenerResource, serviceName)
   203  		}
   204  	}
   205  }
   206  
   207  func (a *authority) watchRouteConfig(routeName string, cb func(resource.RouteConfigUpdate, error)) (cancel func()) {
   208  	first, cancelF := a.pubsub.WatchRouteConfig(routeName, cb)
   209  	if first {
   210  		a.controller.AddWatch(resource.RouteConfigResource, routeName)
   211  	}
   212  	return func() {
   213  		if cancelF() {
   214  			a.controller.RemoveWatch(resource.RouteConfigResource, routeName)
   215  		}
   216  	}
   217  }
   218  
   219  func (a *authority) watchCluster(clusterName string, cb func(resource.ClusterUpdate, error)) (cancel func()) {
   220  	first, cancelF := a.pubsub.WatchCluster(clusterName, cb)
   221  	if first {
   222  		a.controller.AddWatch(resource.ClusterResource, clusterName)
   223  	}
   224  	return func() {
   225  		if cancelF() {
   226  			a.controller.RemoveWatch(resource.ClusterResource, clusterName)
   227  		}
   228  	}
   229  }
   230  
   231  func (a *authority) watchEndpoints(clusterName string, cb func(resource.EndpointsUpdate, error)) (cancel func()) {
   232  	first, cancelF := a.pubsub.WatchEndpoints(clusterName, cb)
   233  	if first {
   234  		a.controller.AddWatch(resource.EndpointsResource, clusterName)
   235  	}
   236  	return func() {
   237  		if cancelF() {
   238  			a.controller.RemoveWatch(resource.EndpointsResource, clusterName)
   239  		}
   240  	}
   241  }
   242  
   243  func (a *authority) reportLoad(server string) (*load.Store, func()) {
   244  	return a.controller.ReportLoad(server)
   245  }
   246  
   247  func (a *authority) dump(t resource.ResourceType) map[string]resource.UpdateWithMD {
   248  	return a.pubsub.Dump(t)
   249  }