50
50
import org .springframework .amqp .AmqpApplicationContextClosedException ;
51
51
import org .springframework .amqp .AmqpException ;
52
52
import org .springframework .amqp .AmqpTimeoutException ;
53
+ import org .springframework .amqp .rabbit .listener .ActiveObjectCounter ;
53
54
import org .springframework .amqp .support .ConditionalExceptionLogger ;
54
55
import org .springframework .beans .factory .InitializingBean ;
55
56
import org .springframework .jmx .export .annotation .ManagedAttribute ;
@@ -105,6 +106,8 @@ public class CachingConnectionFactory extends AbstractConnectionFactory
105
106
106
107
private static final String DEFAULT_DEFERRED_POOL_PREFIX = "spring-rabbit-deferred-pool-" ;
107
108
109
+ private static final int CHANNEL_EXEC_SHUTDOWN_TIMEOUT = 30 ;
110
+
108
111
/**
109
112
* Create a unique ID for the pool.
110
113
*/
@@ -159,6 +162,8 @@ public enum CacheMode {
159
162
/** Synchronization monitor for the shared Connection. */
160
163
private final Object connectionMonitor = new Object ();
161
164
165
+ private final ActiveObjectCounter <Channel > inFlightAsyncCloses = new ActiveObjectCounter <>();
166
+
162
167
private long channelCheckoutTimeout = 0 ;
163
168
164
169
private CacheMode cacheMode = CacheMode .CHANNEL ;
@@ -177,6 +182,8 @@ public enum CacheMode {
177
182
178
183
private ConditionalExceptionLogger closeExceptionLogger = new DefaultChannelCloseLogger ();
179
184
185
+ private PublisherCallbackChannelFactory publisherChannelFactory = PublisherCallbackChannelImpl .factory ();
186
+
180
187
private volatile boolean active = true ;
181
188
182
189
private volatile boolean initialized ;
@@ -430,6 +437,16 @@ public void setCloseExceptionLogger(ConditionalExceptionLogger closeExceptionLog
430
437
}
431
438
}
432
439
440
+ /**
441
+ * Set the factory to use to create {@link PublisherCallbackChannel} instances.
442
+ * @param publisherChannelFactory the factory.
443
+ * @since 2.1.6
444
+ */
445
+ public void setPublisherChannelFactory (PublisherCallbackChannelFactory publisherChannelFactory ) {
446
+ Assert .notNull (publisherChannelFactory , "'publisherChannelFactory' cannot be null" );
447
+ this .publisherChannelFactory = publisherChannelFactory ;
448
+ }
449
+
433
450
@ Override
434
451
public void afterPropertiesSet () {
435
452
this .initialized = true ;
@@ -651,8 +668,8 @@ else if (this.cacheMode == CacheMode.CONNECTION) {
651
668
return null ; // NOSONAR doCreate will throw an exception
652
669
}
653
670
654
- private Channel doCreateBareChannel (ChannelCachingConnectionProxy connection , boolean transactional ) {
655
- Channel channel = connection .createBareChannel (transactional );
671
+ private Channel doCreateBareChannel (ChannelCachingConnectionProxy conn , boolean transactional ) {
672
+ Channel channel = conn .createBareChannel (transactional );
656
673
if (this .publisherConfirms || this .simplePublisherConfirms ) {
657
674
try {
658
675
channel .confirmSelect ();
@@ -663,7 +680,7 @@ private Channel doCreateBareChannel(ChannelCachingConnectionProxy connection, bo
663
680
}
664
681
if ((this .publisherConfirms || this .publisherReturns )
665
682
&& !(channel instanceof PublisherCallbackChannelImpl )) {
666
- channel = new PublisherCallbackChannelImpl (channel , getChannelsExecutor ());
683
+ channel = this . publisherChannelFactory . createChannel (channel , getChannelsExecutor ());
667
684
}
668
685
if (channel != null ) {
669
686
channel .addShutdownListener (this );
@@ -819,7 +836,18 @@ public final void destroy() {
819
836
if (getContextStopped ()) {
820
837
this .stopped = true ;
821
838
if (this .channelsExecutor != null ) {
822
- this .channelsExecutor .shutdownNow ();
839
+ try {
840
+ if (!this .inFlightAsyncCloses .await (CHANNEL_EXEC_SHUTDOWN_TIMEOUT , TimeUnit .SECONDS )) {
841
+ this .logger .warn ("Async closes are still in-flight: " + this .inFlightAsyncCloses .getCount ());
842
+ }
843
+ this .channelsExecutor .shutdown ();
844
+ if (!this .channelsExecutor .awaitTermination (CHANNEL_EXEC_SHUTDOWN_TIMEOUT , TimeUnit .SECONDS )) {
845
+ this .logger .warn ("Channel executor failed to shut down" );
846
+ }
847
+ }
848
+ catch (@ SuppressWarnings ("unused" ) InterruptedException e ) {
849
+ Thread .currentThread ().interrupt ();
850
+ }
823
851
}
824
852
}
825
853
}
@@ -1276,37 +1304,46 @@ private void physicalClose() throws IOException, TimeoutException {
1276
1304
private void asyncClose () {
1277
1305
ExecutorService executorService = getChannelsExecutor ();
1278
1306
final Channel channel = CachedChannelInvocationHandler .this .target ;
1279
- executorService .execute (() -> {
1280
- try {
1281
- if (CachingConnectionFactory .this .publisherConfirms ) {
1282
- channel .waitForConfirmsOrDie (ASYNC_CLOSE_TIMEOUT );
1283
- }
1284
- else {
1285
- Thread .sleep (ASYNC_CLOSE_TIMEOUT );
1286
- }
1287
- }
1288
- catch (InterruptedException e1 ) {
1289
- Thread .currentThread ().interrupt ();
1290
- }
1291
- catch (Exception e2 ) {
1292
- }
1293
- finally {
1307
+ CachingConnectionFactory .this .inFlightAsyncCloses .add (channel );
1308
+ try {
1309
+ executorService .execute (() -> {
1294
1310
try {
1295
- channel .close ();
1296
- }
1297
- catch (IOException e3 ) {
1311
+ if (CachingConnectionFactory .this .publisherConfirms ) {
1312
+ channel .waitForConfirmsOrDie (ASYNC_CLOSE_TIMEOUT );
1313
+ }
1314
+ else {
1315
+ Thread .sleep (ASYNC_CLOSE_TIMEOUT );
1316
+ }
1298
1317
}
1299
- catch (AlreadyClosedException e4 ) {
1318
+ catch (InterruptedException e1 ) {
1319
+ Thread .currentThread ().interrupt ();
1300
1320
}
1301
- catch (TimeoutException e5 ) {
1321
+ catch (Exception e2 ) {
1302
1322
}
1303
- catch (ShutdownSignalException e6 ) {
1304
- if (!RabbitUtils .isNormalShutdown (e6 )) {
1305
- logger .debug ("Unexpected exception on deferred close" , e6 );
1323
+ finally {
1324
+ try {
1325
+ channel .close ();
1326
+ }
1327
+ catch (IOException e3 ) {
1328
+ }
1329
+ catch (AlreadyClosedException e4 ) {
1330
+ }
1331
+ catch (TimeoutException e5 ) {
1332
+ }
1333
+ catch (ShutdownSignalException e6 ) {
1334
+ if (!RabbitUtils .isNormalShutdown (e6 )) {
1335
+ logger .debug ("Unexpected exception on deferred close" , e6 );
1336
+ }
1337
+ }
1338
+ finally {
1339
+ CachingConnectionFactory .this .inFlightAsyncCloses .release (channel );
1306
1340
}
1307
1341
}
1308
- }
1309
- });
1342
+ });
1343
+ }
1344
+ catch (@ SuppressWarnings ("unused" ) RuntimeException e ) {
1345
+ CachingConnectionFactory .this .inFlightAsyncCloses .release (channel );
1346
+ }
1310
1347
}
1311
1348
1312
1349
}
0 commit comments