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 }