github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go (about) 1 /* 2 * 3 * Copyright 2014, Google Inc. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following disclaimer 14 * in the documentation and/or other materials provided with the 15 * distribution. 16 * * Neither the name of Google Inc. nor the names of its 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 */ 33 34 package grpc 35 36 import ( 37 "errors" 38 "fmt" 39 "net" 40 "strings" 41 "sync" 42 "time" 43 44 "github.com/coreos/rkt/Godeps/_workspace/src/golang.org/x/net/context" 45 "github.com/coreos/rkt/Godeps/_workspace/src/google.golang.org/grpc/credentials" 46 "github.com/coreos/rkt/Godeps/_workspace/src/google.golang.org/grpc/grpclog" 47 "github.com/coreos/rkt/Godeps/_workspace/src/google.golang.org/grpc/transport" 48 ) 49 50 var ( 51 // ErrUnspecTarget indicates that the target address is unspecified. 52 ErrUnspecTarget = errors.New("grpc: target is unspecified") 53 // ErrNoTransportSecurity indicates that there is no transport security 54 // being set for ClientConn. Users should either set one or explicityly 55 // call WithInsecure DialOption to disable security. 56 ErrNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)") 57 // ErrCredentialsMisuse indicates that users want to transmit security infomation 58 // (e.g., oauth2 token) which requires secure connection on an insecure 59 // connection. 60 ErrCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)") 61 // ErrClientConnClosing indicates that the operation is illegal because 62 // the session is closing. 63 ErrClientConnClosing = errors.New("grpc: the client connection is closing") 64 // ErrClientConnTimeout indicates that the connection could not be 65 // established or re-established within the specified timeout. 66 ErrClientConnTimeout = errors.New("grpc: timed out trying to connect") 67 // minimum time to give a connection to complete 68 minConnectTimeout = 20 * time.Second 69 ) 70 71 // dialOptions configure a Dial call. dialOptions are set by the DialOption 72 // values passed to Dial. 73 type dialOptions struct { 74 codec Codec 75 block bool 76 insecure bool 77 copts transport.ConnectOptions 78 } 79 80 // DialOption configures how we set up the connection. 81 type DialOption func(*dialOptions) 82 83 // WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling. 84 func WithCodec(c Codec) DialOption { 85 return func(o *dialOptions) { 86 o.codec = c 87 } 88 } 89 90 // WithBlock returns a DialOption which makes caller of Dial blocks until the underlying 91 // connection is up. Without this, Dial returns immediately and connecting the server 92 // happens in background. 93 func WithBlock() DialOption { 94 return func(o *dialOptions) { 95 o.block = true 96 } 97 } 98 99 func WithInsecure() DialOption { 100 return func(o *dialOptions) { 101 o.insecure = true 102 } 103 } 104 105 // WithTransportCredentials returns a DialOption which configures a 106 // connection level security credentials (e.g., TLS/SSL). 107 func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption { 108 return func(o *dialOptions) { 109 o.copts.AuthOptions = append(o.copts.AuthOptions, creds) 110 } 111 } 112 113 // WithPerRPCCredentials returns a DialOption which sets 114 // credentials which will place auth state on each outbound RPC. 115 func WithPerRPCCredentials(creds credentials.Credentials) DialOption { 116 return func(o *dialOptions) { 117 o.copts.AuthOptions = append(o.copts.AuthOptions, creds) 118 } 119 } 120 121 // WithTimeout returns a DialOption that configures a timeout for dialing a client connection. 122 func WithTimeout(d time.Duration) DialOption { 123 return func(o *dialOptions) { 124 o.copts.Timeout = d 125 } 126 } 127 128 // WithDialer returns a DialOption that specifies a function to use for dialing network addresses. 129 func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption { 130 return func(o *dialOptions) { 131 o.copts.Dialer = f 132 } 133 } 134 135 // WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs. 136 func WithUserAgent(s string) DialOption { 137 return func(o *dialOptions) { 138 o.copts.UserAgent = s 139 } 140 } 141 142 // Dial creates a client connection the given target. 143 func Dial(target string, opts ...DialOption) (*ClientConn, error) { 144 if target == "" { 145 return nil, ErrUnspecTarget 146 } 147 cc := &ClientConn{ 148 target: target, 149 shutdownChan: make(chan struct{}), 150 } 151 for _, opt := range opts { 152 opt(&cc.dopts) 153 } 154 if !cc.dopts.insecure { 155 var ok bool 156 for _, c := range cc.dopts.copts.AuthOptions { 157 if _, ok := c.(credentials.TransportAuthenticator); !ok { 158 continue 159 } 160 ok = true 161 } 162 if !ok { 163 return nil, ErrNoTransportSecurity 164 } 165 } else { 166 for _, c := range cc.dopts.copts.AuthOptions { 167 if c.RequireTransportSecurity() { 168 return nil, ErrCredentialsMisuse 169 } 170 } 171 } 172 colonPos := strings.LastIndex(target, ":") 173 if colonPos == -1 { 174 colonPos = len(target) 175 } 176 cc.authority = target[:colonPos] 177 if cc.dopts.codec == nil { 178 // Set the default codec. 179 cc.dopts.codec = protoCodec{} 180 } 181 cc.stateCV = sync.NewCond(&cc.mu) 182 if cc.dopts.block { 183 if err := cc.resetTransport(false); err != nil { 184 cc.Close() 185 return nil, err 186 } 187 // Start to monitor the error status of transport. 188 go cc.transportMonitor() 189 } else { 190 // Start a goroutine connecting to the server asynchronously. 191 go func() { 192 if err := cc.resetTransport(false); err != nil { 193 grpclog.Printf("Failed to dial %s: %v; please retry.", target, err) 194 cc.Close() 195 return 196 } 197 go cc.transportMonitor() 198 }() 199 } 200 return cc, nil 201 } 202 203 // ConnectivityState indicates the state of a client connection. 204 type ConnectivityState int 205 206 const ( 207 // Idle indicates the ClientConn is idle. 208 Idle ConnectivityState = iota 209 // Connecting indicates the ClienConn is connecting. 210 Connecting 211 // Ready indicates the ClientConn is ready for work. 212 Ready 213 // TransientFailure indicates the ClientConn has seen a failure but expects to recover. 214 TransientFailure 215 // Shutdown indicates the ClientConn has stated shutting down. 216 Shutdown 217 ) 218 219 func (s ConnectivityState) String() string { 220 switch s { 221 case Idle: 222 return "IDLE" 223 case Connecting: 224 return "CONNECTING" 225 case Ready: 226 return "READY" 227 case TransientFailure: 228 return "TRANSIENT_FAILURE" 229 case Shutdown: 230 return "SHUTDOWN" 231 default: 232 panic(fmt.Sprintf("unknown connectivity state: %d", s)) 233 } 234 } 235 236 // ClientConn represents a client connection to an RPC service. 237 type ClientConn struct { 238 target string 239 authority string 240 dopts dialOptions 241 shutdownChan chan struct{} 242 243 mu sync.Mutex 244 state ConnectivityState 245 stateCV *sync.Cond 246 // ready is closed and becomes nil when a new transport is up or failed 247 // due to timeout. 248 ready chan struct{} 249 // Every time a new transport is created, this is incremented by 1. Used 250 // to avoid trying to recreate a transport while the new one is already 251 // under construction. 252 transportSeq int 253 transport transport.ClientTransport 254 } 255 256 // State returns the connectivity state of the ClientConn 257 func (cc *ClientConn) State() ConnectivityState { 258 cc.mu.Lock() 259 defer cc.mu.Unlock() 260 return cc.state 261 } 262 263 // WaitForStateChange blocks until the state changes to something other than the sourceState 264 // or timeout fires. It returns false if timeout fires and true otherwise. 265 func (cc *ClientConn) WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool { 266 start := time.Now() 267 cc.mu.Lock() 268 defer cc.mu.Unlock() 269 if sourceState != cc.state { 270 return true 271 } 272 expired := timeout <= time.Since(start) 273 if expired { 274 return false 275 } 276 done := make(chan struct{}) 277 go func() { 278 select { 279 case <-time.After(timeout - time.Since(start)): 280 cc.mu.Lock() 281 expired = true 282 cc.stateCV.Broadcast() 283 cc.mu.Unlock() 284 case <-done: 285 } 286 }() 287 defer close(done) 288 for sourceState == cc.state { 289 cc.stateCV.Wait() 290 if expired { 291 return false 292 } 293 } 294 return true 295 } 296 297 func (cc *ClientConn) resetTransport(closeTransport bool) error { 298 var retries int 299 start := time.Now() 300 for { 301 cc.mu.Lock() 302 cc.state = Connecting 303 cc.stateCV.Broadcast() 304 t := cc.transport 305 ts := cc.transportSeq 306 // Avoid wait() picking up a dying transport unnecessarily. 307 cc.transportSeq = 0 308 if cc.state == Shutdown { 309 cc.mu.Unlock() 310 return ErrClientConnClosing 311 } 312 cc.mu.Unlock() 313 if closeTransport { 314 t.Close() 315 } 316 // Adjust timeout for the current try. 317 copts := cc.dopts.copts 318 if copts.Timeout < 0 { 319 cc.Close() 320 return ErrClientConnTimeout 321 } 322 if copts.Timeout > 0 { 323 copts.Timeout -= time.Since(start) 324 if copts.Timeout <= 0 { 325 cc.Close() 326 return ErrClientConnTimeout 327 } 328 } 329 sleepTime := backoff(retries) 330 timeout := sleepTime 331 if timeout < minConnectTimeout { 332 timeout = minConnectTimeout 333 } 334 if copts.Timeout == 0 || copts.Timeout > timeout { 335 copts.Timeout = timeout 336 } 337 connectTime := time.Now() 338 newTransport, err := transport.NewClientTransport(cc.target, &copts) 339 if err != nil { 340 cc.mu.Lock() 341 cc.state = TransientFailure 342 cc.stateCV.Broadcast() 343 cc.mu.Unlock() 344 sleepTime -= time.Since(connectTime) 345 if sleepTime < 0 { 346 sleepTime = 0 347 } 348 // Fail early before falling into sleep. 349 if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) { 350 cc.Close() 351 return ErrClientConnTimeout 352 } 353 closeTransport = false 354 time.Sleep(sleepTime) 355 retries++ 356 grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target) 357 continue 358 } 359 cc.mu.Lock() 360 if cc.state == Shutdown { 361 // cc.Close() has been invoked. 362 cc.mu.Unlock() 363 newTransport.Close() 364 return ErrClientConnClosing 365 } 366 cc.state = Ready 367 cc.stateCV.Broadcast() 368 cc.transport = newTransport 369 cc.transportSeq = ts + 1 370 if cc.ready != nil { 371 close(cc.ready) 372 cc.ready = nil 373 } 374 cc.mu.Unlock() 375 return nil 376 } 377 } 378 379 // Run in a goroutine to track the error in transport and create the 380 // new transport if an error happens. It returns when the channel is closing. 381 func (cc *ClientConn) transportMonitor() { 382 for { 383 select { 384 // shutdownChan is needed to detect the teardown when 385 // the ClientConn is idle (i.e., no RPC in flight). 386 case <-cc.shutdownChan: 387 return 388 case <-cc.transport.Error(): 389 cc.mu.Lock() 390 cc.state = TransientFailure 391 cc.stateCV.Broadcast() 392 cc.mu.Unlock() 393 if err := cc.resetTransport(true); err != nil { 394 // The ClientConn is closing. 395 grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err) 396 return 397 } 398 continue 399 } 400 } 401 } 402 403 // When wait returns, either the new transport is up or ClientConn is 404 // closing. Used to avoid working on a dying transport. It updates and 405 // returns the transport and its version when there is no error. 406 func (cc *ClientConn) wait(ctx context.Context, ts int) (transport.ClientTransport, int, error) { 407 for { 408 cc.mu.Lock() 409 switch { 410 case cc.state == Shutdown: 411 cc.mu.Unlock() 412 return nil, 0, ErrClientConnClosing 413 case ts < cc.transportSeq: 414 // Worked on a dying transport. Try the new one immediately. 415 defer cc.mu.Unlock() 416 return cc.transport, cc.transportSeq, nil 417 default: 418 ready := cc.ready 419 if ready == nil { 420 ready = make(chan struct{}) 421 cc.ready = ready 422 } 423 cc.mu.Unlock() 424 select { 425 case <-ctx.Done(): 426 return nil, 0, transport.ContextErr(ctx.Err()) 427 // Wait until the new transport is ready or failed. 428 case <-ready: 429 } 430 } 431 } 432 } 433 434 // Close starts to tear down the ClientConn. Returns ErrClientConnClosing if 435 // it has been closed (mostly due to dial time-out). 436 // TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in 437 // some edge cases (e.g., the caller opens and closes many ClientConn's in a 438 // tight loop. 439 func (cc *ClientConn) Close() error { 440 cc.mu.Lock() 441 defer cc.mu.Unlock() 442 if cc.state == Shutdown { 443 return ErrClientConnClosing 444 } 445 cc.state = Shutdown 446 cc.stateCV.Broadcast() 447 if cc.ready != nil { 448 close(cc.ready) 449 cc.ready = nil 450 } 451 if cc.transport != nil { 452 cc.transport.Close() 453 } 454 if cc.shutdownChan != nil { 455 close(cc.shutdownChan) 456 } 457 return nil 458 }