|
15 | 15 |
|
16 | 16 | package com.rabbitmq.perf; |
17 | 17 |
|
18 | | -import com.rabbitmq.client.AlreadyClosedException; |
19 | | -import com.rabbitmq.client.Connection; |
20 | | -import com.rabbitmq.client.ConnectionFactory; |
21 | | -import com.rabbitmq.client.Recoverable; |
22 | | -import com.rabbitmq.client.RecoveryListener; |
| 18 | +import com.rabbitmq.client.*; |
| 19 | +import com.rabbitmq.client.impl.DefaultExceptionHandler; |
23 | 20 | import com.rabbitmq.client.impl.recovery.AutorecoveringConnection; |
24 | 21 | import org.slf4j.Logger; |
25 | 22 | import org.slf4j.LoggerFactory; |
|
32 | 29 | import java.util.Collection; |
33 | 30 | import java.util.List; |
34 | 31 | import java.util.Random; |
35 | | -import java.util.concurrent.CountDownLatch; |
36 | | -import java.util.concurrent.ExecutionException; |
37 | | -import java.util.concurrent.ExecutorService; |
38 | | -import java.util.concurrent.Executors; |
39 | | -import java.util.concurrent.Future; |
40 | | -import java.util.concurrent.ScheduledExecutorService; |
41 | | -import java.util.concurrent.TimeUnit; |
42 | | -import java.util.concurrent.TimeoutException; |
| 32 | +import java.util.concurrent.*; |
43 | 33 | import java.util.concurrent.atomic.AtomicBoolean; |
44 | 34 | import java.util.function.Function; |
45 | 35 | import java.util.function.Supplier; |
@@ -70,21 +60,27 @@ public class MulticastSet { |
70 | 60 | private final List<String> uris; |
71 | 61 | private final Random random = new Random(); |
72 | 62 | private final CompletionHandler completionHandler; |
| 63 | + private final ShutdownService shutdownService; |
73 | 64 | private ThreadingHandler threadingHandler = new DefaultThreadingHandler(); |
74 | 65 |
|
75 | 66 | public MulticastSet(Stats stats, ConnectionFactory factory, |
76 | 67 | MulticastParams params, List<String> uris, CompletionHandler completionHandler) { |
77 | | - this(stats, factory, params, "perftest", uris, completionHandler); |
| 68 | + this(stats, factory, params, "perftest", uris, completionHandler, new ShutdownService()); |
78 | 69 | } |
79 | 70 |
|
80 | 71 | public MulticastSet(Stats stats, ConnectionFactory factory, |
81 | | - MulticastParams params, String testID, List<String> uris, CompletionHandler completionHandler) { |
| 72 | + MulticastParams params, String testID, List<String> uris, CompletionHandler completionHandler) { |
| 73 | + this(stats, factory, params, testID, uris, completionHandler, new ShutdownService()); |
| 74 | + } |
| 75 | + public MulticastSet(Stats stats, ConnectionFactory factory, |
| 76 | + MulticastParams params, String testID, List<String> uris, CompletionHandler completionHandler, ShutdownService shutdownService) { |
82 | 77 | this.stats = stats; |
83 | 78 | this.factory = factory; |
84 | 79 | this.params = params; |
85 | 80 | this.testID = testID; |
86 | 81 | this.uris = uris; |
87 | 82 | this.completionHandler = completionHandler; |
| 83 | + this.shutdownService = shutdownService; |
88 | 84 | this.params.init(); |
89 | 85 | } |
90 | 86 |
|
@@ -145,9 +141,16 @@ public void run(boolean announceStartup) |
145 | 141 | startConsumers(consumerRunnables); |
146 | 142 | startProducers(producerStates); |
147 | 143 |
|
148 | | - this.completionHandler.waitForCompletion(); |
| 144 | + AutoCloseable shutdownSequence = this.shutdownService.wrap( |
| 145 | + () -> shutdown(configurationConnection, consumerConnections, producerStates, producerConnections) |
| 146 | + ); |
149 | 147 |
|
150 | | - shutdown(configurationConnection, consumerConnections, producerStates, producerConnections); |
| 148 | + this.completionHandler.waitForCompletion(); |
| 149 | + try { |
| 150 | + shutdownSequence.close(); |
| 151 | + } catch (Exception e) { |
| 152 | + throw new RuntimeException(e); |
| 153 | + } |
151 | 154 | } |
152 | 155 |
|
153 | 156 | private Function<Integer, ExecutorService> createConsumersExecutorsFactory() { |
@@ -248,38 +251,29 @@ private void shutdown(Connection configurationConnection, Connection[] consumerC |
248 | 251 | try { |
249 | 252 | LOGGER.debug("Starting test shutdown"); |
250 | 253 | for (AgentState producerState : producerStates) { |
251 | | - producerState.task.cancel(true); |
| 254 | + boolean cancelled = producerState.task.cancel(true); |
| 255 | + LOGGER.debug("Producer has been correctly cancelled: {}", cancelled); |
252 | 256 | } |
253 | 257 |
|
254 | | - try { |
255 | | - LOGGER.debug("Closing configuration connection"); |
256 | | - configurationConnection.close(); |
257 | | - LOGGER.debug("Configuration connection closed"); |
258 | | - } catch (AlreadyClosedException e) { |
259 | | - LOGGER.debug("Configuration connection was already closed"); |
260 | | - } catch (Exception e) { |
261 | | - LOGGER.debug("Error while closing configuration connection: {}", e.getMessage()); |
| 258 | + // we do our best to stop producers before closing their connections |
| 259 | + for (AgentState producerState : producerStates) { |
| 260 | + if (!producerState.task.isDone()) { |
| 261 | + try { |
| 262 | + producerState.task.get(10, TimeUnit.SECONDS); |
| 263 | + } catch (Exception e) { |
| 264 | + LOGGER.debug("Error while waiting for producer to stop: {}. Moving on.", e.getMessage()); |
| 265 | + } |
| 266 | + } |
262 | 267 | } |
263 | 268 |
|
| 269 | + dispose(configurationConnection); |
| 270 | + |
264 | 271 | for (Connection producerConnection : producerConnections) { |
265 | | - try { |
266 | | - producerConnection.close(); |
267 | | - } catch (Exception e) { |
268 | | - // don't do anything, we need to close the other connections |
269 | | - } |
| 272 | + dispose(producerConnection); |
270 | 273 | } |
271 | 274 |
|
272 | 275 | for (Connection consumerConnection : consumerConnections) { |
273 | | - try { |
274 | | - LOGGER.debug("Closing consumer connection {}", consumerConnection.getClientProvidedName()); |
275 | | - consumerConnection.close(); |
276 | | - LOGGER.debug("Consumer connection {} has been closed", consumerConnection.getClientProvidedName()); |
277 | | - } catch (AlreadyClosedException e) { |
278 | | - LOGGER.debug("Consumer connection {} already closed", consumerConnection.getClientProvidedName()); |
279 | | - } catch (Exception e) { |
280 | | - // don't do anything, we need to close the other connections |
281 | | - LOGGER.debug("Error while closing consumer connection {}: {}", consumerConnection.getClientProvidedName(), e.getMessage()); |
282 | | - } |
| 276 | + dispose(consumerConnection); |
283 | 277 | } |
284 | 278 |
|
285 | 279 | LOGGER.debug("Shutting down threading handler"); |
@@ -314,6 +308,21 @@ public void handleRecovery(Recoverable recoverable) { |
314 | 308 | } |
315 | 309 | } |
316 | 310 |
|
| 311 | + private static void dispose(Connection connection) { |
| 312 | + try { |
| 313 | + LOGGER.debug("Closing connection {}", connection.getClientProvidedName()); |
| 314 | + // we need to shutdown, it should not take forever, so we pick a reasonably |
| 315 | + // small timeout (3 seconds) |
| 316 | + connection.close(AMQP.REPLY_SUCCESS, "Closed by PerfTest", 3000); |
| 317 | + LOGGER.debug("Connection {} has been closed", connection.getClientProvidedName()); |
| 318 | + } catch (AlreadyClosedException e) { |
| 319 | + LOGGER.debug("Connection {} already closed", connection.getClientProvidedName()); |
| 320 | + } catch (Exception e) { |
| 321 | + // don't do anything, we need to close the other connections |
| 322 | + LOGGER.debug("Error while closing connection {}: {}", connection.getClientProvidedName(), e.getMessage()); |
| 323 | + } |
| 324 | + } |
| 325 | + |
317 | 326 | private void setUri() throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException { |
318 | 327 | if (uris != null) { |
319 | 328 | factory.setUri(uri()); |
|
0 commit comments