gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/balancer/grpclb/grpclb_picker.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 grpclb 20 21 import ( 22 "sync" 23 "sync/atomic" 24 25 "gitee.com/ks-custle/core-gm/grpc/balancer" 26 lbpb "gitee.com/ks-custle/core-gm/grpc/balancer/grpclb/grpc_lb_v1" 27 "gitee.com/ks-custle/core-gm/grpc/codes" 28 "gitee.com/ks-custle/core-gm/grpc/internal/grpcrand" 29 "gitee.com/ks-custle/core-gm/grpc/status" 30 ) 31 32 // rpcStats is same as lbpb.ClientStats, except that numCallsDropped is a map 33 // instead of a slice. 34 type rpcStats struct { 35 // Only access the following fields atomically. 36 numCallsStarted int64 37 numCallsFinished int64 38 numCallsFinishedWithClientFailedToSend int64 39 numCallsFinishedKnownReceived int64 40 41 mu sync.Mutex 42 // map load_balance_token -> num_calls_dropped 43 numCallsDropped map[string]int64 44 } 45 46 func newRPCStats() *rpcStats { 47 return &rpcStats{ 48 numCallsDropped: make(map[string]int64), 49 } 50 } 51 52 func isZeroStats(stats *lbpb.ClientStats) bool { 53 return len(stats.CallsFinishedWithDrop) == 0 && 54 stats.NumCallsStarted == 0 && 55 stats.NumCallsFinished == 0 && 56 stats.NumCallsFinishedWithClientFailedToSend == 0 && 57 stats.NumCallsFinishedKnownReceived == 0 58 } 59 60 // toClientStats converts rpcStats to lbpb.ClientStats, and clears rpcStats. 61 func (s *rpcStats) toClientStats() *lbpb.ClientStats { 62 stats := &lbpb.ClientStats{ 63 NumCallsStarted: atomic.SwapInt64(&s.numCallsStarted, 0), 64 NumCallsFinished: atomic.SwapInt64(&s.numCallsFinished, 0), 65 NumCallsFinishedWithClientFailedToSend: atomic.SwapInt64(&s.numCallsFinishedWithClientFailedToSend, 0), 66 NumCallsFinishedKnownReceived: atomic.SwapInt64(&s.numCallsFinishedKnownReceived, 0), 67 } 68 s.mu.Lock() 69 dropped := s.numCallsDropped 70 s.numCallsDropped = make(map[string]int64) 71 s.mu.Unlock() 72 for token, count := range dropped { 73 stats.CallsFinishedWithDrop = append(stats.CallsFinishedWithDrop, &lbpb.ClientStatsPerToken{ 74 LoadBalanceToken: token, 75 NumCalls: count, 76 }) 77 } 78 return stats 79 } 80 81 func (s *rpcStats) drop(token string) { 82 atomic.AddInt64(&s.numCallsStarted, 1) 83 s.mu.Lock() 84 s.numCallsDropped[token]++ 85 s.mu.Unlock() 86 atomic.AddInt64(&s.numCallsFinished, 1) 87 } 88 89 func (s *rpcStats) failedToSend() { 90 atomic.AddInt64(&s.numCallsStarted, 1) 91 atomic.AddInt64(&s.numCallsFinishedWithClientFailedToSend, 1) 92 atomic.AddInt64(&s.numCallsFinished, 1) 93 } 94 95 func (s *rpcStats) knownReceived() { 96 atomic.AddInt64(&s.numCallsStarted, 1) 97 atomic.AddInt64(&s.numCallsFinishedKnownReceived, 1) 98 atomic.AddInt64(&s.numCallsFinished, 1) 99 } 100 101 type errPicker struct { 102 // Pick always returns this err. 103 err error 104 } 105 106 func (p *errPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 107 return balancer.PickResult{}, p.err 108 } 109 110 // rrPicker does roundrobin on subConns. It's typically used when there's no 111 // response from remote balancer, and grpclb falls back to the resolved 112 // backends. 113 // 114 // It guaranteed that len(subConns) > 0. 115 type rrPicker struct { 116 mu sync.Mutex 117 subConns []balancer.SubConn // The subConns that were READY when taking the snapshot. 118 subConnsNext int 119 } 120 121 func newRRPicker(readySCs []balancer.SubConn) *rrPicker { 122 return &rrPicker{ 123 subConns: readySCs, 124 subConnsNext: grpcrand.Intn(len(readySCs)), 125 } 126 } 127 128 func (p *rrPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 129 p.mu.Lock() 130 defer p.mu.Unlock() 131 sc := p.subConns[p.subConnsNext] 132 p.subConnsNext = (p.subConnsNext + 1) % len(p.subConns) 133 return balancer.PickResult{SubConn: sc}, nil 134 } 135 136 // lbPicker does two layers of picks: 137 // 138 // First layer: roundrobin on all servers in serverList, including drops and backends. 139 // - If it picks a drop, the RPC will fail as being dropped. 140 // - If it picks a backend, do a second layer pick to pick the real backend. 141 // 142 // Second layer: roundrobin on all READY backends. 143 // 144 // It's guaranteed that len(serverList) > 0. 145 type lbPicker struct { 146 mu sync.Mutex 147 serverList []*lbpb.Server 148 serverListNext int 149 subConns []balancer.SubConn // The subConns that were READY when taking the snapshot. 150 subConnsNext int 151 152 stats *rpcStats 153 } 154 155 func newLBPicker(serverList []*lbpb.Server, readySCs []balancer.SubConn, stats *rpcStats) *lbPicker { 156 return &lbPicker{ 157 serverList: serverList, 158 subConns: readySCs, 159 subConnsNext: grpcrand.Intn(len(readySCs)), 160 stats: stats, 161 } 162 } 163 164 func (p *lbPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 165 p.mu.Lock() 166 defer p.mu.Unlock() 167 168 // Layer one roundrobin on serverList. 169 s := p.serverList[p.serverListNext] 170 p.serverListNext = (p.serverListNext + 1) % len(p.serverList) 171 172 // If it's a drop, return an error and fail the RPC. 173 if s.Drop { 174 p.stats.drop(s.LoadBalanceToken) 175 return balancer.PickResult{}, status.Errorf(codes.Unavailable, "request dropped by grpclb") 176 } 177 178 // If not a drop but there's no ready subConns. 179 if len(p.subConns) <= 0 { 180 return balancer.PickResult{}, balancer.ErrNoSubConnAvailable 181 } 182 183 // Return the next ready subConn in the list, also collect rpc stats. 184 sc := p.subConns[p.subConnsNext] 185 p.subConnsNext = (p.subConnsNext + 1) % len(p.subConns) 186 done := func(info balancer.DoneInfo) { 187 if !info.BytesSent { 188 p.stats.failedToSend() 189 } else if info.BytesReceived { 190 p.stats.knownReceived() 191 } 192 } 193 return balancer.PickResult{SubConn: sc, Done: done}, nil 194 } 195 196 func (p *lbPicker) updateReadySCs(readySCs []balancer.SubConn) { 197 p.mu.Lock() 198 defer p.mu.Unlock() 199 200 p.subConns = readySCs 201 p.subConnsNext = p.subConnsNext % len(readySCs) 202 }