1/* 2 * 3 * Copyright 2016 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 19package grpc 20 21import ( 22 "fmt" 23 "net" 24 "sync" 25 26 "golang.org/x/net/context" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/credentials" 29 "google.golang.org/grpc/grpclog" 30 "google.golang.org/grpc/naming" 31 "google.golang.org/grpc/status" 32) 33 34// Address represents a server the client connects to. 35// 36// Deprecated: please use package balancer. 37type Address struct { 38 // Addr is the server address on which a connection will be established. 39 Addr string 40 // Metadata is the information associated with Addr, which may be used 41 // to make load balancing decision. 42 Metadata interface{} 43} 44 45// BalancerConfig specifies the configurations for Balancer. 46// 47// Deprecated: please use package balancer. 48type BalancerConfig struct { 49 // DialCreds is the transport credential the Balancer implementation can 50 // use to dial to a remote load balancer server. The Balancer implementations 51 // can ignore this if it does not need to talk to another party securely. 52 DialCreds credentials.TransportCredentials 53 // Dialer is the custom dialer the Balancer implementation can use to dial 54 // to a remote load balancer server. The Balancer implementations 55 // can ignore this if it doesn't need to talk to remote balancer. 56 Dialer func(context.Context, string) (net.Conn, error) 57} 58 59// BalancerGetOptions configures a Get call. 60// 61// Deprecated: please use package balancer. 62type BalancerGetOptions struct { 63 // BlockingWait specifies whether Get should block when there is no 64 // connected address. 65 BlockingWait bool 66} 67 68// Balancer chooses network addresses for RPCs. 69// 70// Deprecated: please use package balancer. 71type Balancer interface { 72 // Start does the initialization work to bootstrap a Balancer. For example, 73 // this function may start the name resolution and watch the updates. It will 74 // be called when dialing. 75 Start(target string, config BalancerConfig) error 76 // Up informs the Balancer that gRPC has a connection to the server at 77 // addr. It returns down which is called once the connection to addr gets 78 // lost or closed. 79 // TODO: It is not clear how to construct and take advantage of the meaningful error 80 // parameter for down. Need realistic demands to guide. 81 Up(addr Address) (down func(error)) 82 // Get gets the address of a server for the RPC corresponding to ctx. 83 // i) If it returns a connected address, gRPC internals issues the RPC on the 84 // connection to this address; 85 // ii) If it returns an address on which the connection is under construction 86 // (initiated by Notify(...)) but not connected, gRPC internals 87 // * fails RPC if the RPC is fail-fast and connection is in the TransientFailure or 88 // Shutdown state; 89 // or 90 // * issues RPC on the connection otherwise. 91 // iii) If it returns an address on which the connection does not exist, gRPC 92 // internals treats it as an error and will fail the corresponding RPC. 93 // 94 // Therefore, the following is the recommended rule when writing a custom Balancer. 95 // If opts.BlockingWait is true, it should return a connected address or 96 // block if there is no connected address. It should respect the timeout or 97 // cancellation of ctx when blocking. If opts.BlockingWait is false (for fail-fast 98 // RPCs), it should return an address it has notified via Notify(...) immediately 99 // instead of blocking. 100 // 101 // The function returns put which is called once the rpc has completed or failed. 102 // put can collect and report RPC stats to a remote load balancer. 103 // 104 // This function should only return the errors Balancer cannot recover by itself. 105 // gRPC internals will fail the RPC if an error is returned. 106 Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) 107 // Notify returns a channel that is used by gRPC internals to watch the addresses 108 // gRPC needs to connect. The addresses might be from a name resolver or remote 109 // load balancer. gRPC internals will compare it with the existing connected 110 // addresses. If the address Balancer notified is not in the existing connected 111 // addresses, gRPC starts to connect the address. If an address in the existing 112 // connected addresses is not in the notification list, the corresponding connection 113 // is shutdown gracefully. Otherwise, there are no operations to take. Note that 114 // the Address slice must be the full list of the Addresses which should be connected. 115 // It is NOT delta. 116 Notify() <-chan []Address 117 // Close shuts down the balancer. 118 Close() error 119} 120 121// downErr implements net.Error. It is constructed by gRPC internals and passed to the down 122// call of Balancer. 123type downErr struct { 124 timeout bool 125 temporary bool 126 desc string 127} 128 129func (e downErr) Error() string { return e.desc } 130func (e downErr) Timeout() bool { return e.timeout } 131func (e downErr) Temporary() bool { return e.temporary } 132 133func downErrorf(timeout, temporary bool, format string, a ...interface{}) downErr { 134 return downErr{ 135 timeout: timeout, 136 temporary: temporary, 137 desc: fmt.Sprintf(format, a...), 138 } 139} 140 141// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch 142// the name resolution updates and updates the addresses available correspondingly. 143// 144// Deprecated: please use package balancer/roundrobin. 145func RoundRobin(r naming.Resolver) Balancer { 146 return &roundRobin{r: r} 147} 148 149type addrInfo struct { 150 addr Address 151 connected bool 152} 153 154type roundRobin struct { 155 r naming.Resolver 156 w naming.Watcher 157 addrs []*addrInfo // all the addresses the client should potentially connect 158 mu sync.Mutex 159 addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to. 160 next int // index of the next address to return for Get() 161 waitCh chan struct{} // the channel to block when there is no connected address available 162 done bool // The Balancer is closed. 163} 164 165func (rr *roundRobin) watchAddrUpdates() error { 166 updates, err := rr.w.Next() 167 if err != nil { 168 grpclog.Warningf("grpc: the naming watcher stops working due to %v.", err) 169 return err 170 } 171 rr.mu.Lock() 172 defer rr.mu.Unlock() 173 for _, update := range updates { 174 addr := Address{ 175 Addr: update.Addr, 176 Metadata: update.Metadata, 177 } 178 switch update.Op { 179 case naming.Add: 180 var exist bool 181 for _, v := range rr.addrs { 182 if addr == v.addr { 183 exist = true 184 grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr) 185 break 186 } 187 } 188 if exist { 189 continue 190 } 191 rr.addrs = append(rr.addrs, &addrInfo{addr: addr}) 192 case naming.Delete: 193 for i, v := range rr.addrs { 194 if addr == v.addr { 195 copy(rr.addrs[i:], rr.addrs[i+1:]) 196 rr.addrs = rr.addrs[:len(rr.addrs)-1] 197 break 198 } 199 } 200 default: 201 grpclog.Errorln("Unknown update.Op ", update.Op) 202 } 203 } 204 // Make a copy of rr.addrs and write it onto rr.addrCh so that gRPC internals gets notified. 205 open := make([]Address, len(rr.addrs)) 206 for i, v := range rr.addrs { 207 open[i] = v.addr 208 } 209 if rr.done { 210 return ErrClientConnClosing 211 } 212 select { 213 case <-rr.addrCh: 214 default: 215 } 216 rr.addrCh <- open 217 return nil 218} 219 220func (rr *roundRobin) Start(target string, config BalancerConfig) error { 221 rr.mu.Lock() 222 defer rr.mu.Unlock() 223 if rr.done { 224 return ErrClientConnClosing 225 } 226 if rr.r == nil { 227 // If there is no name resolver installed, it is not needed to 228 // do name resolution. In this case, target is added into rr.addrs 229 // as the only address available and rr.addrCh stays nil. 230 rr.addrs = append(rr.addrs, &addrInfo{addr: Address{Addr: target}}) 231 return nil 232 } 233 w, err := rr.r.Resolve(target) 234 if err != nil { 235 return err 236 } 237 rr.w = w 238 rr.addrCh = make(chan []Address, 1) 239 go func() { 240 for { 241 if err := rr.watchAddrUpdates(); err != nil { 242 return 243 } 244 } 245 }() 246 return nil 247} 248 249// Up sets the connected state of addr and sends notification if there are pending 250// Get() calls. 251func (rr *roundRobin) Up(addr Address) func(error) { 252 rr.mu.Lock() 253 defer rr.mu.Unlock() 254 var cnt int 255 for _, a := range rr.addrs { 256 if a.addr == addr { 257 if a.connected { 258 return nil 259 } 260 a.connected = true 261 } 262 if a.connected { 263 cnt++ 264 } 265 } 266 // addr is only one which is connected. Notify the Get() callers who are blocking. 267 if cnt == 1 && rr.waitCh != nil { 268 close(rr.waitCh) 269 rr.waitCh = nil 270 } 271 return func(err error) { 272 rr.down(addr, err) 273 } 274} 275 276// down unsets the connected state of addr. 277func (rr *roundRobin) down(addr Address, err error) { 278 rr.mu.Lock() 279 defer rr.mu.Unlock() 280 for _, a := range rr.addrs { 281 if addr == a.addr { 282 a.connected = false 283 break 284 } 285 } 286} 287 288// Get returns the next addr in the rotation. 289func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) { 290 var ch chan struct{} 291 rr.mu.Lock() 292 if rr.done { 293 rr.mu.Unlock() 294 err = ErrClientConnClosing 295 return 296 } 297 298 if len(rr.addrs) > 0 { 299 if rr.next >= len(rr.addrs) { 300 rr.next = 0 301 } 302 next := rr.next 303 for { 304 a := rr.addrs[next] 305 next = (next + 1) % len(rr.addrs) 306 if a.connected { 307 addr = a.addr 308 rr.next = next 309 rr.mu.Unlock() 310 return 311 } 312 if next == rr.next { 313 // Has iterated all the possible address but none is connected. 314 break 315 } 316 } 317 } 318 if !opts.BlockingWait { 319 if len(rr.addrs) == 0 { 320 rr.mu.Unlock() 321 err = status.Errorf(codes.Unavailable, "there is no address available") 322 return 323 } 324 // Returns the next addr on rr.addrs for failfast RPCs. 325 addr = rr.addrs[rr.next].addr 326 rr.next++ 327 rr.mu.Unlock() 328 return 329 } 330 // Wait on rr.waitCh for non-failfast RPCs. 331 if rr.waitCh == nil { 332 ch = make(chan struct{}) 333 rr.waitCh = ch 334 } else { 335 ch = rr.waitCh 336 } 337 rr.mu.Unlock() 338 for { 339 select { 340 case <-ctx.Done(): 341 err = ctx.Err() 342 return 343 case <-ch: 344 rr.mu.Lock() 345 if rr.done { 346 rr.mu.Unlock() 347 err = ErrClientConnClosing 348 return 349 } 350 351 if len(rr.addrs) > 0 { 352 if rr.next >= len(rr.addrs) { 353 rr.next = 0 354 } 355 next := rr.next 356 for { 357 a := rr.addrs[next] 358 next = (next + 1) % len(rr.addrs) 359 if a.connected { 360 addr = a.addr 361 rr.next = next 362 rr.mu.Unlock() 363 return 364 } 365 if next == rr.next { 366 // Has iterated all the possible address but none is connected. 367 break 368 } 369 } 370 } 371 // The newly added addr got removed by Down() again. 372 if rr.waitCh == nil { 373 ch = make(chan struct{}) 374 rr.waitCh = ch 375 } else { 376 ch = rr.waitCh 377 } 378 rr.mu.Unlock() 379 } 380 } 381} 382 383func (rr *roundRobin) Notify() <-chan []Address { 384 return rr.addrCh 385} 386 387func (rr *roundRobin) Close() error { 388 rr.mu.Lock() 389 defer rr.mu.Unlock() 390 if rr.done { 391 return errBalancerClosed 392 } 393 rr.done = true 394 if rr.w != nil { 395 rr.w.Close() 396 } 397 if rr.waitCh != nil { 398 close(rr.waitCh) 399 rr.waitCh = nil 400 } 401 if rr.addrCh != nil { 402 close(rr.addrCh) 403 } 404 return nil 405} 406 407// pickFirst is used to test multi-addresses in one addrConn in which all addresses share the same addrConn. 408// It is a wrapper around roundRobin balancer. The logic of all methods works fine because balancer.Get() 409// returns the only address Up by resetTransport(). 410type pickFirst struct { 411 *roundRobin 412} 413 414func pickFirstBalancerV1(r naming.Resolver) Balancer { 415 return &pickFirst{&roundRobin{r: r}} 416} 417