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 ;
@@ -108,20 +106,16 @@ final class TransportSet implements WithLogId {
108
106
/*
109
107
* The transport for new outgoing requests.
110
108
* - If shutdown == true, activeTransport is null (shutdown)
111
- * - Otherwise, if delayedTransport != null ,
112
- * activeTransport is delayedTransport (waiting to connect)
109
+ * - Otherwise, if a connection is pending or connecting ,
110
+ * activeTransport is a DelayedClientTransport
113
111
* - Otherwise, activeTransport is either null (initially or when idle)
114
- * or points to a real transport (when connecting or connected ).
112
+ * or points to a real transport (when ready ).
115
113
*
116
114
* 'lock' must be held when assigning to it.
117
115
*/
118
116
@ Nullable
119
117
private volatile ManagedClientTransport activeTransport ;
120
118
121
- @ GuardedBy ("lock" )
122
- @ Nullable
123
- private DelayedClientTransport delayedTransport ;
124
-
125
119
TransportSet (EquivalentAddressGroup addressGroup , String authority ,
126
120
LoadBalancer <ClientTransport > loadBalancer , BackoffPolicy .Provider backoffPolicyProvider ,
127
121
ClientTransportFactory transportFactory , ScheduledExecutorService scheduledExecutor ,
@@ -155,40 +149,24 @@ final ClientTransport obtainActiveTransport() {
155
149
if (savedTransport != null ) {
156
150
return savedTransport ;
157
151
}
158
- Callable <ClientTransport > immediateConnectionTask = null ;
159
152
synchronized (lock ) {
160
153
// Check again, since it could have changed before acquiring the lock
161
154
if (activeTransport == null ) {
162
155
if (shutdown ) {
163
156
return SHUTDOWN_TRANSPORT ;
164
157
}
165
- delayedTransport = new DelayedClientTransport ();
158
+ DelayedClientTransport delayedTransport = new DelayedClientTransport ();
166
159
transports .add (delayedTransport );
167
160
delayedTransport .start (new BaseTransportListener (delayedTransport ));
168
161
activeTransport = delayedTransport ;
169
- immediateConnectionTask = scheduleConnection ();
170
- }
171
- savedTransport = activeTransport ;
172
- }
173
- if (immediateConnectionTask != null ) {
174
- try {
175
- return immediateConnectionTask .call ();
176
- } catch (Exception e ) {
177
- throw Throwables .propagate (e );
162
+ scheduleConnection (delayedTransport );
178
163
}
164
+ return activeTransport ;
179
165
}
180
- return savedTransport ;
181
166
}
182
167
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
168
@ GuardedBy ("lock" )
191
- private Callable < ClientTransport > scheduleConnection () {
169
+ private void scheduleConnection (final DelayedClientTransport delayedTransport ) {
192
170
Preconditions .checkState (reconnectTask == null || reconnectTask .isDone (),
193
171
"previous reconnectTask is not done" );
194
172
@@ -203,47 +181,23 @@ private Callable<ClientTransport> scheduleConnection() {
203
181
nextAddressIndex = 0 ;
204
182
}
205
183
206
- final Callable < ClientTransport > createTransportCallable = new Callable < ClientTransport > () {
184
+ Runnable createTransportRunnable = new Runnable () {
207
185
@ Override
208
- public ClientTransport call () {
209
- DelayedClientTransport savedDelayedTransport ;
210
- ManagedClientTransport newActiveTransport ;
211
- boolean savedShutdown ;
186
+ public void run () {
212
187
synchronized (lock ) {
213
- savedShutdown = shutdown ;
214
188
reconnectTask = null ;
215
189
if (currentAddressIndex == 0 ) {
216
190
backoffWatch .reset ().start ();
217
191
}
218
- newActiveTransport = transportFactory .newClientTransport (address , authority );
192
+ ManagedClientTransport transport =
193
+ transportFactory .newClientTransport (address , authority );
219
194
if (log .isLoggable (Level .FINE )) {
220
195
log .log (Level .FINE , "[{0}] Created {1} for {2}" ,
221
- new Object [] {getLogId (), newActiveTransport .getLogId (), address });
196
+ new Object [] {getLogId (), transport .getLogId (), address });
222
197
}
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 ();
198
+ transports .add (transport );
199
+ transport .start (new TransportListener (transport , delayedTransport , address ));
245
200
}
246
- return newActiveTransport ;
247
201
}
248
202
};
249
203
@@ -266,20 +220,10 @@ public ClientTransport call() {
266
220
if (delayMillis <= 0 ) {
267
221
reconnectTask = null ;
268
222
// No back-off this time.
269
- // Note createTransportRunnable is not supposed to run under the lock.
270
- return createTransportCallable ;
223
+ createTransportRunnable .run ();
271
224
} else {
272
225
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 ;
226
+ createTransportRunnable , delayMillis , TimeUnit .MILLISECONDS );
283
227
}
284
228
}
285
229
@@ -301,7 +245,6 @@ final void shutdown() {
301
245
if (transports .isEmpty ()) {
302
246
runCallback = true ;
303
247
Preconditions .checkState (reconnectTask == null , "Should have no reconnectTask scheduled" );
304
- Preconditions .checkState (delayedTransport == null , "Should have no delayedTransport" );
305
248
} // else: the callback will be run once all transports have been terminated
306
249
}
307
250
if (savedActiveTransport != null ) {
@@ -361,14 +304,13 @@ public void transportTerminated() {
361
304
/** Listener for real transports. */
362
305
private class TransportListener extends BaseTransportListener {
363
306
private final SocketAddress address ;
307
+ private final DelayedClientTransport delayedTransport ;
364
308
365
- public TransportListener (ManagedClientTransport transport , SocketAddress address ) {
309
+ public TransportListener (ManagedClientTransport transport ,
310
+ DelayedClientTransport delayedTransport , SocketAddress address ) {
366
311
super (transport );
367
312
this .address = address ;
368
- }
369
-
370
- private boolean isAttachedToActiveTransport () {
371
- return activeTransport == transport ;
313
+ this .delayedTransport = delayedTransport ;
372
314
}
373
315
374
316
@ Override
@@ -378,11 +320,28 @@ public void transportReady() {
378
320
new Object [] {getLogId (), transport .getLogId (), address });
379
321
}
380
322
super .transportReady ();
323
+ boolean savedShutdown ;
381
324
synchronized (lock ) {
382
- if (isAttachedToActiveTransport ()) {
383
- firstAttempt = true ;
325
+ savedShutdown = shutdown ;
326
+ firstAttempt = true ;
327
+ if (shutdown ) {
328
+ // If TransportSet already shutdown, transport is only to take care of pending
329
+ // streams in delayedTransport, but will not serve new streams, and it will be shutdown
330
+ // as soon as it's set to the delayedTransport.
331
+ // activeTransport should have already been set to null by shutdown(). We keep it null.
332
+ Preconditions .checkState (activeTransport == null ,
333
+ "Unexpected non-null activeTransport" );
334
+ } else if (activeTransport == delayedTransport ) {
335
+ activeTransport = transport ;
384
336
}
385
337
}
338
+ delayedTransport .setTransport (transport );
339
+ // This delayed transport will terminate and be removed from transports.
340
+ delayedTransport .shutdown ();
341
+ if (savedShutdown ) {
342
+ // See comments in the synchronized block above on why we shutdown here.
343
+ transport .shutdown ();
344
+ }
386
345
loadBalancer .handleTransportReady (addressGroup );
387
346
}
388
347
@@ -394,8 +353,18 @@ public void transportShutdown(Status s) {
394
353
}
395
354
super .transportShutdown (s );
396
355
synchronized (lock ) {
397
- if (isAttachedToActiveTransport () ) {
356
+ if (activeTransport == transport ) {
398
357
activeTransport = null ;
358
+ } else if (activeTransport == delayedTransport ) {
359
+ // Continue reconnect if there are still addresses to try.
360
+ // Fail if all addresses have been tried and failed in a row.
361
+ if (nextAddressIndex == 0 ) {
362
+ delayedTransport .setTransport (new FailingClientTransport (s ));
363
+ delayedTransport .shutdown ();
364
+ activeTransport = null ;
365
+ } else {
366
+ scheduleConnection (delayedTransport );
367
+ }
399
368
}
400
369
}
401
370
loadBalancer .handleTransportShutdown (addressGroup , s );
@@ -408,9 +377,9 @@ public void transportTerminated() {
408
377
new Object [] {getLogId (), transport .getLogId (), address });
409
378
}
410
379
super .transportTerminated ();
411
- Preconditions .checkState (! isAttachedToActiveTransport () ,
412
- "Listener is still attached to activeTransport . "
413
- + "Seems transportTerminated was not called." );
380
+ Preconditions .checkState (activeTransport != transport ,
381
+ "activeTransport still points to the delayedTransport . "
382
+ + "Seems transportShutdown() was not called." );
414
383
}
415
384
}
416
385
0 commit comments