dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/controller/controller.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 controller contains implementation to connect to the control plane.
    25  // Including starting the ClientConn, starting the xDS stream, and
    26  // sending/receiving messages.
    27  //
    28  // All the messages are parsed by the resource package (e.g.
    29  // UnmarshalListener()) and sent to the Pubsub watchers.
    30  package controller
    31  
    32  import (
    33  	"context"
    34  	"errors"
    35  	"fmt"
    36  	"sync"
    37  	"time"
    38  )
    39  
    40  import (
    41  	dubbogoLogger "github.com/dubbogo/gost/log/logger"
    42  
    43  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    44  
    45  	_struct "github.com/golang/protobuf/ptypes/struct"
    46  
    47  	"google.golang.org/grpc"
    48  	"google.golang.org/grpc/keepalive"
    49  )
    50  
    51  import (
    52  	"dubbo.apache.org/dubbo-go/v3/xds/client/bootstrap"
    53  	"dubbo.apache.org/dubbo-go/v3/xds/client/controller/version"
    54  	"dubbo.apache.org/dubbo-go/v3/xds/client/pubsub"
    55  	"dubbo.apache.org/dubbo-go/v3/xds/client/resource"
    56  	"dubbo.apache.org/dubbo-go/v3/xds/utils/backoff"
    57  	"dubbo.apache.org/dubbo-go/v3/xds/utils/buffer"
    58  )
    59  
    60  // Controller manages the connection and stream to the control plane.
    61  //
    62  // It keeps track of what resources are being watched, and send new requests
    63  // when new watches are added.
    64  //
    65  // It takes a pubsub (as an interface) as input. When a response is received,
    66  // it's parsed, and the updates are sent to the pubsub.
    67  type Controller struct {
    68  	config          *bootstrap.ServerConfig
    69  	updateHandler   pubsub.UpdateHandler
    70  	updateValidator resource.UpdateValidatorFunc
    71  	logger          dubbogoLogger.Logger
    72  
    73  	cc               *grpc.ClientConn // Connection to the management server.
    74  	vClient          version.MetadataWrappedVersionClient
    75  	stopRunGoroutine context.CancelFunc
    76  
    77  	backoff  func(int) time.Duration
    78  	streamCh chan grpc.ClientStream
    79  	sendCh   *buffer.Unbounded
    80  
    81  	mu sync.Mutex
    82  	// Message specific watch infos, protected by the above mutex. These are
    83  	// written to, after successfully reading from the update channel, and are
    84  	// read from when recovering from a broken stream to resend the xDS
    85  	// messages. When the user of this client object cancels a watch call,
    86  	// these are set to nil. All accesses to the map protected and any value
    87  	// inside the map should be protected with the above mutex.
    88  	watchMap map[resource.ResourceType]map[string]bool
    89  	// versionMap contains the version that was acked (the version in the ack
    90  	// request that was sent on wire). The key is rType, the value is the
    91  	// version string, becaues the versions for different resource types should
    92  	// be independent.
    93  	versionMap map[resource.ResourceType]string
    94  	// nonceMap contains the nonce from the most recent received response.
    95  	nonceMap map[resource.ResourceType]string
    96  
    97  	// Changes to map lrsClients and the lrsClient inside the map need to be
    98  	// protected by lrsMu.
    99  	//
   100  	// TODO: after LRS refactoring, each controller should only manage the LRS
   101  	// stream to its server. LRS streams to other servers should be managed by
   102  	// other controllers.
   103  	lrsMu      sync.Mutex
   104  	lrsClients map[string]*lrsClient
   105  }
   106  
   107  // New creates a new controller.
   108  func New(config *bootstrap.ServerConfig, updateHandler pubsub.UpdateHandler, validator resource.UpdateValidatorFunc, logger dubbogoLogger.Logger) (_ *Controller, retErr error) {
   109  	switch {
   110  	case config == nil:
   111  		return nil, errors.New("xds: no xds_server provided")
   112  	case config.ServerURI == "":
   113  		return nil, errors.New("xds: no xds_server name provided in options")
   114  	case config.Creds == nil:
   115  		return nil, errors.New("xds: no credentials provided in options")
   116  	case config.NodeProto == nil:
   117  		return nil, errors.New("xds: no node_proto provided in options")
   118  	}
   119  
   120  	dopts := []grpc.DialOption{
   121  		config.Creds,
   122  		grpc.WithKeepaliveParams(keepalive.ClientParameters{
   123  			Time:    5 * time.Minute,
   124  			Timeout: 20 * time.Second,
   125  		}),
   126  	}
   127  
   128  	ret := &Controller{
   129  		config:          config,
   130  		updateValidator: validator,
   131  		updateHandler:   updateHandler,
   132  		logger:          logger,
   133  		backoff:         backoff.DefaultExponential.Backoff, // TODO: should this be configurable?
   134  		streamCh:        make(chan grpc.ClientStream, 1),
   135  		sendCh:          buffer.NewUnbounded(),
   136  		watchMap:        make(map[resource.ResourceType]map[string]bool),
   137  		versionMap:      make(map[resource.ResourceType]string),
   138  		nonceMap:        make(map[resource.ResourceType]string),
   139  
   140  		lrsClients: make(map[string]*lrsClient),
   141  	}
   142  
   143  	defer func() {
   144  		if retErr != nil {
   145  			ret.Close()
   146  		}
   147  	}()
   148  
   149  	cc, err := grpc.Dial(config.ServerURI, dopts...)
   150  	if err != nil {
   151  		// An error from a non-blocking dial indicates something serious.
   152  		return nil, fmt.Errorf("xds: failed to dial control plane {%s}: %v", config.ServerURI, err)
   153  	}
   154  	ret.cc = cc
   155  
   156  	builder := version.GetAPIClientBuilder(config.TransportAPI)
   157  	if builder == nil {
   158  		return nil, fmt.Errorf("no client builder for xDS API version: %v", config.TransportAPI)
   159  	}
   160  	apiClient, err := builder(version.BuildOptions{NodeProto: config.NodeProto, Logger: logger})
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	ret.vClient = apiClient
   165  
   166  	ctx, cancel := context.WithCancel(context.Background())
   167  	ret.stopRunGoroutine = cancel
   168  	go ret.run(ctx)
   169  
   170  	return ret, nil
   171  }
   172  
   173  func (t *Controller) SetMetadata(m *_struct.Struct) error {
   174  	dopts := []grpc.DialOption{
   175  		t.config.Creds,
   176  		grpc.WithKeepaliveParams(keepalive.ClientParameters{
   177  			Time:    5 * time.Minute,
   178  			Timeout: 20 * time.Second,
   179  		}),
   180  	}
   181  
   182  	cc, err := grpc.Dial(t.config.ServerURI, dopts...)
   183  	if err != nil {
   184  		// An error from a non-blocking dial indicates something serious.
   185  		return fmt.Errorf("xds: failed to dial control plane {%s}: %v", t.config.ServerURI, err)
   186  	}
   187  	t.cc = cc
   188  	builder := version.GetAPIClientBuilder(t.config.TransportAPI)
   189  	if builder == nil {
   190  		return fmt.Errorf("no client builder for xDS API version: %v", t.config.TransportAPI)
   191  	}
   192  	v3PBNode, ok := t.config.NodeProto.(*v3corepb.Node)
   193  	if ok {
   194  		v3PBNode.Metadata = m
   195  	}
   196  	apiClient, err := builder(version.BuildOptions{NodeProto: t.config.NodeProto, Logger: t.logger})
   197  	if err != nil {
   198  		return err
   199  	}
   200  	t.vClient = apiClient
   201  	t.stopRunGoroutine()
   202  	ctx, cancel := context.WithCancel(context.Background())
   203  	t.stopRunGoroutine = cancel
   204  	go t.run(ctx)
   205  	return nil
   206  }
   207  
   208  // Close closes the controller.
   209  func (t *Controller) Close() {
   210  	// Note that Close needs to check for nils even if some of them are always
   211  	// set in the constructor. This is because the constructor defers Close() in
   212  	// error cases, and the fields might not be set when the error happens.
   213  	if t.stopRunGoroutine != nil {
   214  		t.stopRunGoroutine()
   215  	}
   216  	if t.cc != nil {
   217  		t.cc.Close()
   218  	}
   219  }