34
34
import com .google .common .annotations .VisibleForTesting ;
35
35
import com .google .common .base .Preconditions ;
36
36
import com .google .common .base .Stopwatch ;
37
- import com .google .common .base .Throwables ;
38
37
39
38
import io .grpc .EquivalentAddressGroup ;
40
39
import io .grpc .LoadBalancer ;
44
43
import java .util .ArrayList ;
45
44
import java .util .Collection ;
46
45
import java .util .List ;
47
- import java .util .concurrent .Callable ;
48
46
import java .util .concurrent .ScheduledExecutorService ;
49
47
import java .util .concurrent .ScheduledFuture ;
50
48
import java .util .concurrent .TimeUnit ;
59
57
* Transports for a single {@link SocketAddress}.
60
58
*/
61
59
@ ThreadSafe
62
- final class TransportSet implements WithLogId {
60
+ final class TransportSet {
63
61
private static final Logger log = Logger .getLogger (TransportSet .class .getName ());
62
+
64
63
private static final ClientTransport SHUTDOWN_TRANSPORT =
65
64
new FailingClientTransport (Status .UNAVAILABLE .withDescription ("TransportSet is shutdown" ));
66
65
@@ -108,20 +107,16 @@ final class TransportSet implements WithLogId {
108
107
/*
109
108
* The transport for new outgoing requests.
110
109
* - If shutdown == true, activeTransport is null (shutdown)
111
- * - Otherwise, if delayedTransport != null ,
112
- * activeTransport is delayedTransport (waiting to connect)
110
+ * - Otherwise, if a connection is pending or connecting ,
111
+ * activeTransport is a DelayedClientTransport
113
112
* - Otherwise, activeTransport is either null (initially or when idle)
114
- * or points to a real transport (when connecting or connected ).
113
+ * or points to a real transport (when ready ).
115
114
*
116
115
* 'lock' must be held when assigning to it.
117
116
*/
118
117
@ Nullable
119
118
private volatile ManagedClientTransport activeTransport ;
120
119
121
- @ GuardedBy ("lock" )
122
- @ Nullable
123
- private DelayedClientTransport delayedTransport ;
124
-
125
120
TransportSet (EquivalentAddressGroup addressGroup , String authority ,
126
121
LoadBalancer <ClientTransport > loadBalancer , BackoffPolicy .Provider backoffPolicyProvider ,
127
122
ClientTransportFactory transportFactory , ScheduledExecutorService scheduledExecutor ,
@@ -155,40 +150,24 @@ final ClientTransport obtainActiveTransport() {
155
150
if (savedTransport != null ) {
156
151
return savedTransport ;
157
152
}
158
- Callable <ClientTransport > immediateConnectionTask = null ;
159
153
synchronized (lock ) {
160
154
// Check again, since it could have changed before acquiring the lock
161
155
if (activeTransport == null ) {
162
156
if (shutdown ) {
163
157
return SHUTDOWN_TRANSPORT ;
164
158
}
165
- delayedTransport = new DelayedClientTransport ();
159
+ DelayedClientTransport delayedTransport = new DelayedClientTransport ();
166
160
transports .add (delayedTransport );
167
161
delayedTransport .start (new BaseTransportListener (delayedTransport ));
168
162
activeTransport = delayedTransport ;
169
- immediateConnectionTask = scheduleConnection ();
163
+ scheduleConnection (delayedTransport );
170
164
}
171
- savedTransport = activeTransport ;
165
+ return activeTransport ;
172
166
}
173
- if (immediateConnectionTask != null ) {
174
- try {
175
- return immediateConnectionTask .call ();
176
- } catch (Exception e ) {
177
- throw Throwables .propagate (e );
178
- }
179
- }
180
- return savedTransport ;
181
167
}
182
168
183
- /**
184
- * Schedule a task that creates a new transport.
185
- *
186
- * @return if not {@code null}, caller should run the returned callable outside of lock. The
187
- * callable returns the real transport that has been created.
188
- */
189
- @ Nullable
190
169
@ GuardedBy ("lock" )
191
- private Callable < ClientTransport > scheduleConnection () {
170
+ private void scheduleConnection (final DelayedClientTransport delayedTransport ) {
192
171
Preconditions .checkState (reconnectTask == null || reconnectTask .isDone (),
193
172
"previous reconnectTask is not done" );
194
173
@@ -203,47 +182,20 @@ private Callable<ClientTransport> scheduleConnection() {
203
182
nextAddressIndex = 0 ;
204
183
}
205
184
206
- final Callable < ClientTransport > createTransportCallable = new Callable < ClientTransport > () {
185
+ Runnable createTransportRunnable = new Runnable () {
207
186
@ Override
208
- public ClientTransport call () {
209
- DelayedClientTransport savedDelayedTransport ;
210
- ManagedClientTransport newActiveTransport ;
211
- boolean savedShutdown ;
187
+ public void run () {
212
188
synchronized (lock ) {
213
- savedShutdown = shutdown ;
214
189
reconnectTask = null ;
215
190
if (currentAddressIndex == 0 ) {
216
191
backoffWatch .reset ().start ();
217
192
}
218
- newActiveTransport = transportFactory .newClientTransport (address , authority );
219
- if (log .isLoggable (Level .FINE )) {
220
- log .log (Level .FINE , "[{0}] Created {1} for {2}" ,
221
- new Object [] {getLogId (), newActiveTransport .getLogId (), address });
222
- }
223
- transports .add (newActiveTransport );
224
- newActiveTransport .start (
225
- new TransportListener (newActiveTransport , address ));
226
- if (shutdown ) {
227
- // If TransportSet already shutdown, newActiveTransport is only to take care of pending
228
- // streams in delayedTransport, but will not serve new streams, and it will be shutdown
229
- // as soon as it's set to the delayedTransport.
230
- // activeTransport should have already been set to null by shutdown(). We keep it null.
231
- Preconditions .checkState (activeTransport == null ,
232
- "Unexpected non-null activeTransport" );
233
- } else {
234
- activeTransport = newActiveTransport ;
235
- }
236
- savedDelayedTransport = delayedTransport ;
237
- delayedTransport = null ;
238
- }
239
- savedDelayedTransport .setTransport (newActiveTransport );
240
- // This delayed transport will terminate and be removed from transports.
241
- savedDelayedTransport .shutdown ();
242
- if (savedShutdown ) {
243
- // See comments in the synchronized block above on why we shutdown here.
244
- newActiveTransport .shutdown ();
193
+ ManagedClientTransport transport =
194
+ transportFactory .newClientTransport (address , authority );
195
+ log .log (Level .FINE , "Created transport {0} for {1}" , new Object [] {transport , address });
196
+ transports .add (transport );
197
+ transport .start (new TransportListener (transport , delayedTransport , address ));
245
198
}
246
- return newActiveTransport ;
247
199
}
248
200
};
249
201
@@ -259,27 +211,13 @@ public ClientTransport call() {
259
211
}
260
212
}
261
213
firstAttempt = false ;
262
- if (log .isLoggable (Level .FINE )) {
263
- log .log (Level .FINE , "[{0}] Scheduling connection after {1} ms for {2}" ,
264
- new Object []{getLogId (), delayMillis , address });
265
- }
266
214
if (delayMillis <= 0 ) {
267
215
reconnectTask = null ;
268
216
// No back-off this time.
269
- // Note createTransportRunnable is not supposed to run under the lock.
270
- return createTransportCallable ;
217
+ createTransportRunnable .run ();
271
218
} else {
272
219
reconnectTask = scheduledExecutor .schedule (
273
- new Runnable () {
274
- @ Override public void run () {
275
- try {
276
- createTransportCallable .call ();
277
- } catch (Exception e ) {
278
- throw Throwables .propagate (e );
279
- }
280
- }
281
- }, delayMillis , TimeUnit .MILLISECONDS );
282
- return null ;
220
+ createTransportRunnable , delayMillis , TimeUnit .MILLISECONDS );
283
221
}
284
222
}
285
223
@@ -301,7 +239,6 @@ final void shutdown() {
301
239
if (transports .isEmpty ()) {
302
240
runCallback = true ;
303
241
Preconditions .checkState (reconnectTask == null , "Should have no reconnectTask scheduled" );
304
- Preconditions .checkState (delayedTransport == null , "Should have no delayedTransport" );
305
242
} // else: the callback will be run once all transports have been terminated
306
243
}
307
244
if (savedActiveTransport != null ) {
@@ -320,11 +257,6 @@ private void cancelReconnectTask() {
320
257
}
321
258
}
322
259
323
- @ Override
324
- public String getLogId () {
325
- return GrpcUtil .getLogId (this );
326
- }
327
-
328
260
/** Shared base for both delayed and real transports. */
329
261
private class BaseTransportListener implements ManagedClientTransport .Listener {
330
262
protected final ManagedClientTransport transport ;
@@ -345,9 +277,6 @@ public void transportTerminated() {
345
277
synchronized (lock ) {
346
278
transports .remove (transport );
347
279
if (shutdown && transports .isEmpty ()) {
348
- if (log .isLoggable (Level .FINE )) {
349
- log .log (Level .FINE , "[{0}] Terminated" , getLogId ());
350
- }
351
280
runCallback = true ;
352
281
cancelReconnectTask ();
353
282
}
@@ -361,56 +290,75 @@ public void transportTerminated() {
361
290
/** Listener for real transports. */
362
291
private class TransportListener extends BaseTransportListener {
363
292
private final SocketAddress address ;
293
+ private final DelayedClientTransport delayedTransport ;
364
294
365
- public TransportListener (ManagedClientTransport transport , SocketAddress address ) {
295
+ public TransportListener (ManagedClientTransport transport ,
296
+ DelayedClientTransport delayedTransport , SocketAddress address ) {
366
297
super (transport );
367
298
this .address = address ;
368
- }
369
-
370
- private boolean isAttachedToActiveTransport () {
371
- return activeTransport == transport ;
299
+ this .delayedTransport = delayedTransport ;
372
300
}
373
301
374
302
@ Override
375
303
public void transportReady () {
376
- if (log .isLoggable (Level .FINE )) {
377
- log .log (Level .FINE , "[{0}] {1} for {2} is ready" ,
378
- new Object [] {getLogId (), transport .getLogId (), address });
379
- }
304
+ log .log (Level .FINE , "Transport {0} for {1} is ready" , new Object [] {transport , address });
380
305
super .transportReady ();
306
+ boolean savedShutdown ;
381
307
synchronized (lock ) {
382
- if (isAttachedToActiveTransport ()) {
383
- firstAttempt = true ;
308
+ savedShutdown = shutdown ;
309
+ firstAttempt = true ;
310
+ if (shutdown ) {
311
+ // If TransportSet already shutdown, transport is only to take care of pending
312
+ // streams in delayedTransport, but will not serve new streams, and it will be shutdown
313
+ // as soon as it's set to the delayedTransport.
314
+ // activeTransport should have already been set to null by shutdown(). We keep it null.
315
+ Preconditions .checkState (activeTransport == null ,
316
+ "Unexpected non-null activeTransport" );
317
+ } else if (activeTransport == delayedTransport ) {
318
+ activeTransport = transport ;
384
319
}
385
320
}
321
+ delayedTransport .setTransport (transport );
322
+ // This delayed transport will terminate and be removed from transports.
323
+ delayedTransport .shutdown ();
324
+ if (savedShutdown ) {
325
+ // See comments in the synchronized block above on why we shutdown here.
326
+ transport .shutdown ();
327
+ }
386
328
loadBalancer .handleTransportReady (addressGroup );
387
329
}
388
330
389
331
@ Override
390
332
public void transportShutdown (Status s ) {
391
- if (log .isLoggable (Level .FINE )) {
392
- log .log (Level .FINE , "[{0}] {1} for {2} is being shutdown with status {3}" ,
393
- new Object [] {getLogId (), transport .getLogId (), address , s });
394
- }
333
+ log .log (Level .FINE , "Transport {0} for {1} is being shutdown with {2}" ,
334
+ new Object [] {transport , address , s });
395
335
super .transportShutdown (s );
396
336
synchronized (lock ) {
397
- if (isAttachedToActiveTransport () ) {
337
+ if (activeTransport == transport ) {
398
338
activeTransport = null ;
339
+ } else if (activeTransport == delayedTransport ) {
340
+ // Continue reconnect if there are still addresses to try.
341
+ // Fail if all addresses have been tried and failed in a row.
342
+ if (nextAddressIndex == 0 ) {
343
+ delayedTransport .setTransport (new FailingClientTransport (s ));
344
+ delayedTransport .shutdown ();
345
+ activeTransport = null ;
346
+ } else {
347
+ scheduleConnection (delayedTransport );
348
+ }
399
349
}
400
350
}
401
351
loadBalancer .handleTransportShutdown (addressGroup , s );
402
352
}
403
353
404
354
@ Override
405
355
public void transportTerminated () {
406
- if (log .isLoggable (Level .FINE )) {
407
- log .log (Level .FINE , "[{0}] {1} for {2} is terminated" ,
408
- new Object [] {getLogId (), transport .getLogId (), address });
409
- }
356
+ log .log (Level .FINE , "Transport {0} for {1} is terminated" ,
357
+ new Object [] {transport , address });
410
358
super .transportTerminated ();
411
- Preconditions .checkState (! isAttachedToActiveTransport () ,
412
- "Listener is still attached to activeTransport . "
413
- + "Seems transportTerminated was not called." );
359
+ Preconditions .checkState (activeTransport != transport ,
360
+ "activeTransport still points to the delayedTransport . "
361
+ + "Seems transportShutdown() was not called." );
414
362
}
415
363
}
416
364
0 commit comments