github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/priority/balancer.go (about) 1 /* 2 * 3 * Copyright 2021 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 priority implements the priority balancer. 20 // 21 // This balancer will be kept in internal until we use it in the xds balancers, 22 // and are confident its functionalities are stable. It will then be exported 23 // for more users. 24 package priority 25 26 import ( 27 "encoding/json" 28 "fmt" 29 "sync" 30 "time" 31 32 "github.com/hxx258456/ccgo/grpc/balancer" 33 "github.com/hxx258456/ccgo/grpc/internal/balancergroup" 34 "github.com/hxx258456/ccgo/grpc/internal/buffer" 35 "github.com/hxx258456/ccgo/grpc/internal/grpclog" 36 "github.com/hxx258456/ccgo/grpc/internal/grpcsync" 37 "github.com/hxx258456/ccgo/grpc/internal/hierarchy" 38 "github.com/hxx258456/ccgo/grpc/internal/pretty" 39 "github.com/hxx258456/ccgo/grpc/resolver" 40 "github.com/hxx258456/ccgo/grpc/serviceconfig" 41 ) 42 43 // Name is the name of the priority balancer. 44 const Name = "priority_experimental" 45 46 func init() { 47 balancer.Register(bb{}) 48 } 49 50 type bb struct{} 51 52 func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer { 53 b := &priorityBalancer{ 54 cc: cc, 55 done: grpcsync.NewEvent(), 56 childToPriority: make(map[string]int), 57 children: make(map[string]*childBalancer), 58 childBalancerStateUpdate: buffer.NewUnbounded(), 59 } 60 61 b.logger = prefixLogger(b) 62 b.bg = balancergroup.New(cc, bOpts, b, b.logger) 63 b.bg.Start() 64 go b.run() 65 b.logger.Infof("Created") 66 return b 67 } 68 69 func (b bb) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 70 return parseConfig(s) 71 } 72 73 func (bb) Name() string { 74 return Name 75 } 76 77 // timerWrapper wraps a timer with a boolean. So that when a race happens 78 // between AfterFunc and Stop, the func is guaranteed to not execute. 79 type timerWrapper struct { 80 stopped bool 81 timer *time.Timer 82 } 83 84 type priorityBalancer struct { 85 logger *grpclog.PrefixLogger 86 cc balancer.ClientConn 87 bg *balancergroup.BalancerGroup 88 done *grpcsync.Event 89 childBalancerStateUpdate *buffer.Unbounded 90 91 mu sync.Mutex 92 childInUse string 93 // priority of the child that's current in use. Int starting from 0, and 0 94 // is the higher priority. 95 priorityInUse int 96 // priorities is a list of child names from higher to lower priority. 97 priorities []string 98 // childToPriority is a map from the child name to it's priority. Priority 99 // is an int start from 0, and 0 is the higher priority. 100 childToPriority map[string]int 101 // children is a map from child name to sub-balancers. 102 children map[string]*childBalancer 103 // The timer to give a priority some time to connect. And if the priority 104 // doesn't go into Ready/Failure, the next priority will be started. 105 // 106 // One timer is enough because there can be at most one priority in init 107 // state. 108 priorityInitTimer *timerWrapper 109 } 110 111 func (b *priorityBalancer) UpdateClientConnState(s balancer.ClientConnState) error { 112 b.logger.Infof("Received update from resolver, balancer config: %+v", pretty.ToJSON(s.BalancerConfig)) 113 newConfig, ok := s.BalancerConfig.(*LBConfig) 114 if !ok { 115 return fmt.Errorf("unexpected balancer config with type: %T", s.BalancerConfig) 116 } 117 addressesSplit := hierarchy.Group(s.ResolverState.Addresses) 118 119 b.mu.Lock() 120 defer b.mu.Unlock() 121 // Create and remove children, since we know all children from the config 122 // are used by some priority. 123 for name, newSubConfig := range newConfig.Children { 124 bb := balancer.Get(newSubConfig.Config.Name) 125 if bb == nil { 126 b.logger.Errorf("balancer name %v from config is not registered", newSubConfig.Config.Name) 127 continue 128 } 129 130 currentChild, ok := b.children[name] 131 if !ok { 132 // This is a new child, add it to the children list. But note that 133 // the balancer isn't built, because this child can be a low 134 // priority. If necessary, it will be built when syncing priorities. 135 cb := newChildBalancer(name, b, bb) 136 cb.updateConfig(newSubConfig, resolver.State{ 137 Addresses: addressesSplit[name], 138 ServiceConfig: s.ResolverState.ServiceConfig, 139 Attributes: s.ResolverState.Attributes, 140 }) 141 b.children[name] = cb 142 continue 143 } 144 145 // This is not a new child. But the config/addresses could change. 146 147 // The balancing policy name is changed, close the old child. But don't 148 // rebuild, rebuild will happen when syncing priorities. 149 if currentChild.bb.Name() != bb.Name() { 150 currentChild.stop() 151 currentChild.updateBuilder(bb) 152 } 153 154 // Update config and address, but note that this doesn't send the 155 // updates to child balancer (the child balancer might not be built, if 156 // it's a low priority). 157 currentChild.updateConfig(newSubConfig, resolver.State{ 158 Addresses: addressesSplit[name], 159 ServiceConfig: s.ResolverState.ServiceConfig, 160 Attributes: s.ResolverState.Attributes, 161 }) 162 } 163 164 // Remove child from children if it's not in new config. 165 for name, oldChild := range b.children { 166 if _, ok := newConfig.Children[name]; !ok { 167 oldChild.stop() 168 } 169 } 170 171 // Update priorities and handle priority changes. 172 b.priorities = newConfig.Priorities 173 b.childToPriority = make(map[string]int, len(newConfig.Priorities)) 174 for pi, pName := range newConfig.Priorities { 175 b.childToPriority[pName] = pi 176 } 177 // Sync the states of all children to the new updated priorities. This 178 // include starting/stopping child balancers when necessary. 179 b.syncPriority() 180 181 return nil 182 } 183 184 func (b *priorityBalancer) ResolverError(err error) { 185 b.bg.ResolverError(err) 186 } 187 188 func (b *priorityBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 189 b.bg.UpdateSubConnState(sc, state) 190 } 191 192 func (b *priorityBalancer) Close() { 193 b.bg.Close() 194 195 b.mu.Lock() 196 defer b.mu.Unlock() 197 b.done.Fire() 198 // Clear states of the current child in use, so if there's a race in picker 199 // update, it will be dropped. 200 b.childInUse = "" 201 b.stopPriorityInitTimer() 202 } 203 204 func (b *priorityBalancer) ExitIdle() { 205 b.bg.ExitIdle() 206 } 207 208 // stopPriorityInitTimer stops the priorityInitTimer if it's not nil, and set it 209 // to nil. 210 // 211 // Caller must hold b.mu. 212 func (b *priorityBalancer) stopPriorityInitTimer() { 213 timerW := b.priorityInitTimer 214 if timerW == nil { 215 return 216 } 217 b.priorityInitTimer = nil 218 timerW.stopped = true 219 timerW.timer.Stop() 220 } 221 222 // UpdateState implements balancergroup.BalancerStateAggregator interface. The 223 // balancer group sends new connectivity state and picker here. 224 func (b *priorityBalancer) UpdateState(childName string, state balancer.State) { 225 b.childBalancerStateUpdate.Put(&childBalancerState{ 226 name: childName, 227 s: state, 228 }) 229 } 230 231 type childBalancerState struct { 232 name string 233 s balancer.State 234 } 235 236 // run handles child update in a separate goroutine, so if the child sends 237 // updates inline (when called by parent), it won't cause deadlocks (by trying 238 // to hold the same mutex). 239 func (b *priorityBalancer) run() { 240 for { 241 select { 242 case u := <-b.childBalancerStateUpdate.Get(): 243 b.childBalancerStateUpdate.Load() 244 s := u.(*childBalancerState) 245 // Needs to handle state update in a goroutine, because each state 246 // update needs to start/close child policy, could result in 247 // deadlock. 248 b.handleChildStateUpdate(s.name, s.s) 249 case <-b.done.Done(): 250 return 251 } 252 } 253 }