github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/balancer/base/balancer.go (about) 1 /* 2 * 3 * Copyright 2017 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 base 20 21 import ( 22 "errors" 23 "fmt" 24 25 "github.com/hxx258456/ccgo/grpc/balancer" 26 "github.com/hxx258456/ccgo/grpc/connectivity" 27 "github.com/hxx258456/ccgo/grpc/grpclog" 28 "github.com/hxx258456/ccgo/grpc/resolver" 29 ) 30 31 var logger = grpclog.Component("balancer") 32 33 type baseBuilder struct { 34 name string 35 pickerBuilder PickerBuilder 36 config Config 37 } 38 39 func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { 40 bal := &baseBalancer{ 41 cc: cc, 42 pickerBuilder: bb.pickerBuilder, 43 44 subConns: resolver.NewAddressMap(), 45 scStates: make(map[balancer.SubConn]connectivity.State), 46 csEvltr: &balancer.ConnectivityStateEvaluator{}, 47 config: bb.config, 48 } 49 // Initialize picker to a picker that always returns 50 // ErrNoSubConnAvailable, because when state of a SubConn changes, we 51 // may call UpdateState with this picker. 52 bal.picker = NewErrPicker(balancer.ErrNoSubConnAvailable) 53 return bal 54 } 55 56 func (bb *baseBuilder) Name() string { 57 return bb.name 58 } 59 60 type baseBalancer struct { 61 cc balancer.ClientConn 62 pickerBuilder PickerBuilder 63 64 csEvltr *balancer.ConnectivityStateEvaluator 65 state connectivity.State 66 67 subConns *resolver.AddressMap 68 scStates map[balancer.SubConn]connectivity.State 69 picker balancer.Picker 70 config Config 71 72 resolverErr error // the last error reported by the resolver; cleared on successful resolution 73 connErr error // the last connection error; cleared upon leaving TransientFailure 74 } 75 76 func (b *baseBalancer) ResolverError(err error) { 77 b.resolverErr = err 78 if b.subConns.Len() == 0 { 79 b.state = connectivity.TransientFailure 80 } 81 82 if b.state != connectivity.TransientFailure { 83 // The picker will not change since the balancer does not currently 84 // report an error. 85 return 86 } 87 b.regeneratePicker() 88 b.cc.UpdateState(balancer.State{ 89 ConnectivityState: b.state, 90 Picker: b.picker, 91 }) 92 } 93 94 func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error { 95 // TODO: handle s.ResolverState.ServiceConfig? 96 if logger.V(2) { 97 logger.Info("base.baseBalancer: got new ClientConn state: ", s) 98 } 99 // Successful resolution; clear resolver error and ensure we return nil. 100 b.resolverErr = nil 101 // addrsSet is the set converted from addrs, it's used for quick lookup of an address. 102 addrsSet := resolver.NewAddressMap() 103 for _, a := range s.ResolverState.Addresses { 104 addrsSet.Set(a, nil) 105 if _, ok := b.subConns.Get(a); !ok { 106 // a is a new address (not existing in b.subConns). 107 sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck}) 108 if err != nil { 109 logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err) 110 continue 111 } 112 b.subConns.Set(a, sc) 113 b.scStates[sc] = connectivity.Idle 114 b.csEvltr.RecordTransition(connectivity.Shutdown, connectivity.Idle) 115 sc.Connect() 116 } 117 } 118 for _, a := range b.subConns.Keys() { 119 sci, _ := b.subConns.Get(a) 120 sc := sci.(balancer.SubConn) 121 // a was removed by resolver. 122 if _, ok := addrsSet.Get(a); !ok { 123 b.cc.RemoveSubConn(sc) 124 b.subConns.Delete(a) 125 // Keep the state of this sc in b.scStates until sc's state becomes Shutdown. 126 // The entry will be deleted in UpdateSubConnState. 127 } 128 } 129 // If resolver state contains no addresses, return an error so ClientConn 130 // will trigger re-resolve. Also records this as an resolver error, so when 131 // the overall state turns transient failure, the error message will have 132 // the zero address information. 133 if len(s.ResolverState.Addresses) == 0 { 134 b.ResolverError(errors.New("produced zero addresses")) 135 return balancer.ErrBadResolverState 136 } 137 return nil 138 } 139 140 // mergeErrors builds an error from the last connection error and the last 141 // resolver error. Must only be called if b.state is TransientFailure. 142 func (b *baseBalancer) mergeErrors() error { 143 // connErr must always be non-nil unless there are no SubConns, in which 144 // case resolverErr must be non-nil. 145 if b.connErr == nil { 146 return fmt.Errorf("last resolver error: %v", b.resolverErr) 147 } 148 if b.resolverErr == nil { 149 return fmt.Errorf("last connection error: %v", b.connErr) 150 } 151 return fmt.Errorf("last connection error: %v; last resolver error: %v", b.connErr, b.resolverErr) 152 } 153 154 // regeneratePicker takes a snapshot of the balancer, and generates a picker 155 // from it. The picker is 156 // - errPicker if the balancer is in TransientFailure, 157 // - built by the pickerBuilder with all READY SubConns otherwise. 158 func (b *baseBalancer) regeneratePicker() { 159 if b.state == connectivity.TransientFailure { 160 b.picker = NewErrPicker(b.mergeErrors()) 161 return 162 } 163 readySCs := make(map[balancer.SubConn]SubConnInfo) 164 165 // Filter out all ready SCs from full subConn map. 166 for _, addr := range b.subConns.Keys() { 167 sci, _ := b.subConns.Get(addr) 168 sc := sci.(balancer.SubConn) 169 if st, ok := b.scStates[sc]; ok && st == connectivity.Ready { 170 readySCs[sc] = SubConnInfo{Address: addr} 171 } 172 } 173 b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs}) 174 } 175 176 func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 177 s := state.ConnectivityState 178 if logger.V(2) { 179 logger.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s) 180 } 181 oldS, ok := b.scStates[sc] 182 if !ok { 183 if logger.V(2) { 184 logger.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s) 185 } 186 return 187 } 188 if oldS == connectivity.TransientFailure && 189 (s == connectivity.Connecting || s == connectivity.Idle) { 190 // Once a subconn enters TRANSIENT_FAILURE, ignore subsequent IDLE or 191 // CONNECTING transitions to prevent the aggregated state from being 192 // always CONNECTING when many backends exist but are all down. 193 if s == connectivity.Idle { 194 sc.Connect() 195 } 196 return 197 } 198 b.scStates[sc] = s 199 switch s { 200 case connectivity.Idle: 201 sc.Connect() 202 case connectivity.Shutdown: 203 // When an address was removed by resolver, b called RemoveSubConn but 204 // kept the sc's state in scStates. Remove state for this sc here. 205 delete(b.scStates, sc) 206 case connectivity.TransientFailure: 207 // Save error to be reported via picker. 208 b.connErr = state.ConnectionError 209 } 210 211 b.state = b.csEvltr.RecordTransition(oldS, s) 212 213 // Regenerate picker when one of the following happens: 214 // - this sc entered or left ready 215 // - the aggregated state of balancer is TransientFailure 216 // (may need to update error message) 217 if (s == connectivity.Ready) != (oldS == connectivity.Ready) || 218 b.state == connectivity.TransientFailure { 219 b.regeneratePicker() 220 } 221 b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker}) 222 } 223 224 // Close is a nop because base balancer doesn't have internal state to clean up, 225 // and it doesn't need to call RemoveSubConn for the SubConns. 226 func (b *baseBalancer) Close() { 227 } 228 229 // ExitIdle is a nop because the base balancer attempts to stay connected to 230 // all SubConns at all times. 231 func (b *baseBalancer) ExitIdle() { 232 } 233 234 // NewErrPicker returns a Picker that always returns err on Pick(). 235 func NewErrPicker(err error) balancer.Picker { 236 return &errPicker{err: err} 237 } 238 239 // NewErrPickerV2 is temporarily defined for backward compatibility reasons. 240 // 241 // Deprecated: use NewErrPicker instead. 242 var NewErrPickerV2 = NewErrPicker 243 244 type errPicker struct { 245 err error // Pick() always returns this err. 246 } 247 248 func (p *errPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { 249 return balancer.PickResult{}, p.err 250 }