go.etcd.io/etcd@v3.3.27+incompatible/clientv3/balancer/picker/roundrobin_balanced.go (about) 1 // Copyright 2018 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package picker 16 17 import ( 18 "context" 19 "sync" 20 21 "go.uber.org/zap" 22 "go.uber.org/zap/zapcore" 23 "google.golang.org/grpc/balancer" 24 "google.golang.org/grpc/resolver" 25 ) 26 27 // newRoundrobinBalanced returns a new roundrobin balanced picker. 28 func newRoundrobinBalanced(cfg Config) Picker { 29 scs := make([]balancer.SubConn, 0, len(cfg.SubConnToResolverAddress)) 30 for sc := range cfg.SubConnToResolverAddress { 31 scs = append(scs, sc) 32 } 33 return &rrBalanced{ 34 p: RoundrobinBalanced, 35 lg: cfg.Logger, 36 scs: scs, 37 scToAddr: cfg.SubConnToResolverAddress, 38 } 39 } 40 41 type rrBalanced struct { 42 p Policy 43 44 lg *zap.Logger 45 46 mu sync.RWMutex 47 next int 48 scs []balancer.SubConn 49 scToAddr map[balancer.SubConn]resolver.Address 50 } 51 52 func (rb *rrBalanced) String() string { return rb.p.String() } 53 54 // Pick is called for every client request. 55 func (rb *rrBalanced) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { 56 rb.mu.RLock() 57 n := len(rb.scs) 58 rb.mu.RUnlock() 59 if n == 0 { 60 return nil, nil, balancer.ErrNoSubConnAvailable 61 } 62 63 rb.mu.Lock() 64 cur := rb.next 65 sc := rb.scs[cur] 66 picked := rb.scToAddr[sc].Addr 67 rb.next = (rb.next + 1) % len(rb.scs) 68 rb.mu.Unlock() 69 70 rb.lg.Debug( 71 "picked", 72 zap.String("picker", rb.p.String()), 73 zap.String("address", picked), 74 zap.Int("subconn-index", cur), 75 zap.Int("subconn-size", n), 76 ) 77 78 doneFunc := func(info balancer.DoneInfo) { 79 // TODO: error handling? 80 fss := []zapcore.Field{ 81 zap.Error(info.Err), 82 zap.String("picker", rb.p.String()), 83 zap.String("address", picked), 84 zap.Bool("success", info.Err == nil), 85 zap.Bool("bytes-sent", info.BytesSent), 86 zap.Bool("bytes-received", info.BytesReceived), 87 } 88 if info.Err == nil { 89 rb.lg.Debug("balancer done", fss...) 90 } else { 91 rb.lg.Warn("balancer failed", fss...) 92 } 93 } 94 return sc, doneFunc, nil 95 }