blob: e1730166cde61a5aefed53c1f76a72a7eccab4f5 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
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}