pickfirst.go (5095B)
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 grpc 20 21 import ( 22 "errors" 23 "fmt" 24 25 "google.golang.org/grpc/balancer" 26 "google.golang.org/grpc/connectivity" 27 ) 28 29 // PickFirstBalancerName is the name of the pick_first balancer. 30 const PickFirstBalancerName = "pick_first" 31 32 func newPickfirstBuilder() balancer.Builder { 33 return &pickfirstBuilder{} 34 } 35 36 type pickfirstBuilder struct{} 37 38 func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { 39 return &pickfirstBalancer{cc: cc} 40 } 41 42 func (*pickfirstBuilder) Name() string { 43 return PickFirstBalancerName 44 } 45 46 type pickfirstBalancer struct { 47 state connectivity.State 48 cc balancer.ClientConn 49 subConn balancer.SubConn 50 } 51 52 func (b *pickfirstBalancer) ResolverError(err error) { 53 if logger.V(2) { 54 logger.Infof("pickfirstBalancer: ResolverError called with error: %v", err) 55 } 56 if b.subConn == nil { 57 b.state = connectivity.TransientFailure 58 } 59 60 if b.state != connectivity.TransientFailure { 61 // The picker will not change since the balancer does not currently 62 // report an error. 63 return 64 } 65 b.cc.UpdateState(balancer.State{ 66 ConnectivityState: connectivity.TransientFailure, 67 Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)}, 68 }) 69 } 70 71 func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error { 72 if len(state.ResolverState.Addresses) == 0 { 73 // The resolver reported an empty address list. Treat it like an error by 74 // calling b.ResolverError. 75 if b.subConn != nil { 76 // Remove the old subConn. All addresses were removed, so it is no longer 77 // valid. 78 b.cc.RemoveSubConn(b.subConn) 79 b.subConn = nil 80 } 81 b.ResolverError(errors.New("produced zero addresses")) 82 return balancer.ErrBadResolverState 83 } 84 85 if b.subConn != nil { 86 b.cc.UpdateAddresses(b.subConn, state.ResolverState.Addresses) 87 return nil 88 } 89 90 subConn, err := b.cc.NewSubConn(state.ResolverState.Addresses, balancer.NewSubConnOptions{}) 91 if err != nil { 92 if logger.V(2) { 93 logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) 94 } 95 b.state = connectivity.TransientFailure 96 b.cc.UpdateState(balancer.State{ 97 ConnectivityState: connectivity.TransientFailure, 98 Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)}, 99 }) 100 return balancer.ErrBadResolverState 101 } 102 b.subConn = subConn 103 b.state = connectivity.Idle 104 b.cc.UpdateState(balancer.State{ 105 ConnectivityState: connectivity.Connecting, 106 Picker: &picker{err: balancer.ErrNoSubConnAvailable}, 107 }) 108 b.subConn.Connect() 109 return nil 110 } 111 112 func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { 113 if logger.V(2) { 114 logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", subConn, state) 115 } 116 if b.subConn != subConn { 117 if logger.V(2) { 118 logger.Infof("pickfirstBalancer: ignored state change because subConn is not recognized") 119 } 120 return 121 } 122 b.state = state.ConnectivityState 123 if state.ConnectivityState == connectivity.Shutdown { 124 b.subConn = nil 125 return 126 } 127 128 switch state.ConnectivityState { 129 case connectivity.Ready: 130 b.cc.UpdateState(balancer.State{ 131 ConnectivityState: state.ConnectivityState, 132 Picker: &picker{result: balancer.PickResult{SubConn: subConn}}, 133 }) 134 case connectivity.Connecting: 135 b.cc.UpdateState(balancer.State{ 136 ConnectivityState: state.ConnectivityState, 137 Picker: &picker{err: balancer.ErrNoSubConnAvailable}, 138 }) 139 case connectivity.Idle: 140 b.cc.UpdateState(balancer.State{ 141 ConnectivityState: state.ConnectivityState, 142 Picker: &idlePicker{subConn: subConn}, 143 }) 144 case connectivity.TransientFailure: 145 b.cc.UpdateState(balancer.State{ 146 ConnectivityState: state.ConnectivityState, 147 Picker: &picker{err: state.ConnectionError}, 148 }) 149 } 150 } 151 152 func (b *pickfirstBalancer) Close() { 153 } 154 155 func (b *pickfirstBalancer) ExitIdle() { 156 if b.subConn != nil && b.state == connectivity.Idle { 157 b.subConn.Connect() 158 } 159 } 160 161 type picker struct { 162 result balancer.PickResult 163 err error 164 } 165 166 func (p *picker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 167 return p.result, p.err 168 } 169 170 // idlePicker is used when the SubConn is IDLE and kicks the SubConn into 171 // CONNECTING when Pick is called. 172 type idlePicker struct { 173 subConn balancer.SubConn 174 } 175 176 func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 177 i.subConn.Connect() 178 return balancer.PickResult{}, balancer.ErrNoSubConnAvailable 179 } 180 181 func init() { 182 balancer.Register(newPickfirstBuilder()) 183 }