github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterresolver/clusterresolver.go (about) 1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * 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 // Package clusterresolver contains EDS balancer implementation. 20 package clusterresolver 21 22 import ( 23 "encoding/json" 24 "errors" 25 "fmt" 26 27 "github.com/hxx258456/ccgo/grpc/attributes" 28 "github.com/hxx258456/ccgo/grpc/balancer" 29 "github.com/hxx258456/ccgo/grpc/balancer/base" 30 "github.com/hxx258456/ccgo/grpc/connectivity" 31 "github.com/hxx258456/ccgo/grpc/internal/buffer" 32 "github.com/hxx258456/ccgo/grpc/internal/grpclog" 33 "github.com/hxx258456/ccgo/grpc/internal/grpcsync" 34 "github.com/hxx258456/ccgo/grpc/internal/pretty" 35 "github.com/hxx258456/ccgo/grpc/resolver" 36 "github.com/hxx258456/ccgo/grpc/serviceconfig" 37 "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/priority" 38 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient" 39 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 40 ) 41 42 // Name is the name of the cluster_resolver balancer. 43 const Name = "cluster_resolver_experimental" 44 45 var ( 46 errBalancerClosed = errors.New("cdsBalancer is closed") 47 newChildBalancer = func(bb balancer.Builder, cc balancer.ClientConn, o balancer.BuildOptions) balancer.Balancer { 48 return bb.Build(cc, o) 49 } 50 ) 51 52 func init() { 53 balancer.Register(bb{}) 54 } 55 56 type bb struct{} 57 58 // Build helps implement the balancer.Builder interface. 59 func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 60 priorityBuilder := balancer.Get(priority.Name) 61 if priorityBuilder == nil { 62 logger.Errorf("priority balancer is needed but not registered") 63 return nil 64 } 65 priorityConfigParser, ok := priorityBuilder.(balancer.ConfigParser) 66 if !ok { 67 logger.Errorf("priority balancer builder is not a config parser") 68 return nil 69 } 70 71 b := &clusterResolverBalancer{ 72 bOpts: opts, 73 updateCh: buffer.NewUnbounded(), 74 closed: grpcsync.NewEvent(), 75 done: grpcsync.NewEvent(), 76 77 priorityBuilder: priorityBuilder, 78 priorityConfigParser: priorityConfigParser, 79 } 80 b.logger = prefixLogger(b) 81 b.logger.Infof("Created") 82 83 b.resourceWatcher = newResourceResolver(b) 84 b.cc = &ccWrapper{ 85 ClientConn: cc, 86 resourceWatcher: b.resourceWatcher, 87 } 88 89 go b.run() 90 return b 91 } 92 93 func (bb) Name() string { 94 return Name 95 } 96 97 func (bb) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 98 var cfg LBConfig 99 if err := json.Unmarshal(c, &cfg); err != nil { 100 return nil, fmt.Errorf("unable to unmarshal balancer config %s into cluster-resolver config, error: %v", string(c), err) 101 } 102 return &cfg, nil 103 } 104 105 // ccUpdate wraps a clientConn update received from gRPC (pushed from the 106 // xdsResolver). 107 type ccUpdate struct { 108 state balancer.ClientConnState 109 err error 110 } 111 112 // scUpdate wraps a subConn update received from gRPC. This is directly passed 113 // on to the child balancer. 114 type scUpdate struct { 115 subConn balancer.SubConn 116 state balancer.SubConnState 117 } 118 119 type exitIdle struct{} 120 121 // clusterResolverBalancer manages xdsClient and the actual EDS balancer implementation that 122 // does load balancing. 123 // 124 // It currently has only an clusterResolverBalancer. Later, we may add fallback. 125 type clusterResolverBalancer struct { 126 cc balancer.ClientConn 127 bOpts balancer.BuildOptions 128 updateCh *buffer.Unbounded // Channel for updates from gRPC. 129 resourceWatcher *resourceResolver 130 logger *grpclog.PrefixLogger 131 closed *grpcsync.Event 132 done *grpcsync.Event 133 134 priorityBuilder balancer.Builder 135 priorityConfigParser balancer.ConfigParser 136 137 config *LBConfig 138 configRaw *serviceconfig.ParseResult 139 xdsClient xdsclient.XDSClient // xDS client to watch EDS resource. 140 attrsWithClient *attributes.Attributes // Attributes with xdsClient attached to be passed to the child policies. 141 142 child balancer.Balancer 143 priorities []priorityConfig 144 watchUpdateReceived bool 145 } 146 147 // handleClientConnUpdate handles a ClientConnUpdate received from gRPC. Good 148 // updates lead to registration of EDS and DNS watches. Updates with error lead 149 // to cancellation of existing watch and propagation of the same error to the 150 // child balancer. 151 func (b *clusterResolverBalancer) handleClientConnUpdate(update *ccUpdate) { 152 // We first handle errors, if any, and then proceed with handling the 153 // update, only if the status quo has changed. 154 if err := update.err; err != nil { 155 b.handleErrorFromUpdate(err, true) 156 return 157 } 158 159 b.logger.Infof("Receive update from resolver, balancer config: %v", pretty.ToJSON(update.state.BalancerConfig)) 160 cfg, _ := update.state.BalancerConfig.(*LBConfig) 161 if cfg == nil { 162 b.logger.Warningf("xds: unexpected LoadBalancingConfig type: %T", update.state.BalancerConfig) 163 return 164 } 165 166 b.config = cfg 167 b.configRaw = update.state.ResolverState.ServiceConfig 168 b.resourceWatcher.updateMechanisms(cfg.DiscoveryMechanisms) 169 170 if !b.watchUpdateReceived { 171 // If update was not received, wait for it. 172 return 173 } 174 // If eds resp was received before this, the child policy was created. We 175 // need to generate a new balancer config and send it to the child, because 176 // certain fields (unrelated to EDS watch) might have changed. 177 if err := b.updateChildConfig(); err != nil { 178 b.logger.Warningf("failed to update child policy config: %v", err) 179 } 180 } 181 182 // handleWatchUpdate handles a watch update from the xDS Client. Good updates 183 // lead to clientConn updates being invoked on the underlying child balancer. 184 func (b *clusterResolverBalancer) handleWatchUpdate(update *resourceUpdate) { 185 if err := update.err; err != nil { 186 b.logger.Warningf("Watch error from xds-client %p: %v", b.xdsClient, err) 187 b.handleErrorFromUpdate(err, false) 188 return 189 } 190 191 b.logger.Infof("resource update: %+v", pretty.ToJSON(update.priorities)) 192 b.watchUpdateReceived = true 193 b.priorities = update.priorities 194 195 // A new EDS update triggers new child configs (e.g. different priorities 196 // for the priority balancer), and new addresses (the endpoints come from 197 // the EDS response). 198 if err := b.updateChildConfig(); err != nil { 199 b.logger.Warningf("failed to update child policy's balancer config: %v", err) 200 } 201 } 202 203 // updateChildConfig builds a balancer config from eb's cached eds resp and 204 // service config, and sends that to the child balancer. Note that it also 205 // generates the addresses, because the endpoints come from the EDS resp. 206 // 207 // If child balancer doesn't already exist, one will be created. 208 func (b *clusterResolverBalancer) updateChildConfig() error { 209 // Child was build when the first EDS resp was received, so we just build 210 // the config and addresses. 211 if b.child == nil { 212 b.child = newChildBalancer(b.priorityBuilder, b.cc, b.bOpts) 213 } 214 215 childCfgBytes, addrs, err := buildPriorityConfigJSON(b.priorities, b.config.XDSLBPolicy) 216 if err != nil { 217 return fmt.Errorf("failed to build priority balancer config: %v", err) 218 } 219 childCfg, err := b.priorityConfigParser.ParseConfig(childCfgBytes) 220 if err != nil { 221 return fmt.Errorf("failed to parse generated priority balancer config, this should never happen because the config is generated: %v", err) 222 } 223 b.logger.Infof("build balancer config: %v", pretty.ToJSON(childCfg)) 224 return b.child.UpdateClientConnState(balancer.ClientConnState{ 225 ResolverState: resolver.State{ 226 Addresses: addrs, 227 ServiceConfig: b.configRaw, 228 Attributes: b.attrsWithClient, 229 }, 230 BalancerConfig: childCfg, 231 }) 232 } 233 234 // handleErrorFromUpdate handles both the error from parent ClientConn (from CDS 235 // balancer) and the error from xds client (from the watcher). fromParent is 236 // true if error is from parent ClientConn. 237 // 238 // If the error is connection error, it should be handled for fallback purposes. 239 // 240 // If the error is resource-not-found: 241 // - If it's from CDS balancer (shows as a resolver error), it means LDS or CDS 242 // resources were removed. The EDS watch should be canceled. 243 // - If it's from xds client, it means EDS resource were removed. The EDS 244 // watcher should keep watching. 245 // In both cases, the sub-balancers will be receive the error. 246 func (b *clusterResolverBalancer) handleErrorFromUpdate(err error, fromParent bool) { 247 b.logger.Warningf("Received error: %v", err) 248 if fromParent && xdsresource.ErrType(err) == xdsresource.ErrorTypeResourceNotFound { 249 // This is an error from the parent ClientConn (can be the parent CDS 250 // balancer), and is a resource-not-found error. This means the resource 251 // (can be either LDS or CDS) was removed. Stop the EDS watch. 252 b.resourceWatcher.stop() 253 } 254 if b.child != nil { 255 b.child.ResolverError(err) 256 } else { 257 // If eds balancer was never created, fail the RPCs with errors. 258 b.cc.UpdateState(balancer.State{ 259 ConnectivityState: connectivity.TransientFailure, 260 Picker: base.NewErrPicker(err), 261 }) 262 } 263 264 } 265 266 // run is a long-running goroutine which handles all updates from gRPC and 267 // xdsClient. All methods which are invoked directly by gRPC or xdsClient simply 268 // push an update onto a channel which is read and acted upon right here. 269 func (b *clusterResolverBalancer) run() { 270 for { 271 select { 272 case u := <-b.updateCh.Get(): 273 b.updateCh.Load() 274 switch update := u.(type) { 275 case *ccUpdate: 276 b.handleClientConnUpdate(update) 277 case *scUpdate: 278 // SubConn updates are simply handed over to the underlying 279 // child balancer. 280 if b.child == nil { 281 b.logger.Errorf("xds: received scUpdate {%+v} with no child balancer", update) 282 break 283 } 284 b.child.UpdateSubConnState(update.subConn, update.state) 285 case exitIdle: 286 if b.child == nil { 287 b.logger.Errorf("xds: received ExitIdle with no child balancer") 288 break 289 } 290 // This implementation assumes the child balancer supports 291 // ExitIdle (but still checks for the interface's existence to 292 // avoid a panic if not). If the child does not, no subconns 293 // will be connected. 294 if ei, ok := b.child.(balancer.ExitIdler); ok { 295 ei.ExitIdle() 296 } 297 } 298 case u := <-b.resourceWatcher.updateChannel: 299 b.handleWatchUpdate(u) 300 301 // Close results in cancellation of the EDS watch and closing of the 302 // underlying child policy and is the only way to exit this goroutine. 303 case <-b.closed.Done(): 304 b.resourceWatcher.stop() 305 306 if b.child != nil { 307 b.child.Close() 308 b.child = nil 309 } 310 // This is the *ONLY* point of return from this function. 311 b.logger.Infof("Shutdown") 312 b.done.Fire() 313 return 314 } 315 } 316 } 317 318 // Following are methods to implement the balancer interface. 319 320 // UpdateClientConnState receives the serviceConfig (which contains the 321 // clusterName to watch for in CDS) and the xdsClient object from the 322 // xdsResolver. 323 func (b *clusterResolverBalancer) UpdateClientConnState(state balancer.ClientConnState) error { 324 if b.closed.HasFired() { 325 b.logger.Warningf("xds: received ClientConnState {%+v} after clusterResolverBalancer was closed", state) 326 return errBalancerClosed 327 } 328 329 if b.xdsClient == nil { 330 c := xdsclient.FromResolverState(state.ResolverState) 331 if c == nil { 332 return balancer.ErrBadResolverState 333 } 334 b.xdsClient = c 335 b.attrsWithClient = state.ResolverState.Attributes 336 } 337 338 b.updateCh.Put(&ccUpdate{state: state}) 339 return nil 340 } 341 342 // ResolverError handles errors reported by the xdsResolver. 343 func (b *clusterResolverBalancer) ResolverError(err error) { 344 if b.closed.HasFired() { 345 b.logger.Warningf("xds: received resolver error {%v} after clusterResolverBalancer was closed", err) 346 return 347 } 348 b.updateCh.Put(&ccUpdate{err: err}) 349 } 350 351 // UpdateSubConnState handles subConn updates from gRPC. 352 func (b *clusterResolverBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 353 if b.closed.HasFired() { 354 b.logger.Warningf("xds: received subConn update {%v, %v} after clusterResolverBalancer was closed", sc, state) 355 return 356 } 357 b.updateCh.Put(&scUpdate{subConn: sc, state: state}) 358 } 359 360 // Close closes the cdsBalancer and the underlying child balancer. 361 func (b *clusterResolverBalancer) Close() { 362 b.closed.Fire() 363 <-b.done.Done() 364 } 365 366 func (b *clusterResolverBalancer) ExitIdle() { 367 b.updateCh.Put(exitIdle{}) 368 } 369 370 // ccWrapper overrides ResolveNow(), so that re-resolution from the child 371 // policies will trigger the DNS resolver in cluster_resolver balancer. 372 type ccWrapper struct { 373 balancer.ClientConn 374 resourceWatcher *resourceResolver 375 } 376 377 func (c *ccWrapper) ResolveNow(resolver.ResolveNowOptions) { 378 c.resourceWatcher.resolveNow() 379 }