protected void addAndStartConsumers(int delta) { synchronized (this.consumersMonitor) { if (this.consumers != null) { for (int i = 0; i < delta; i++) { BlockingQueueConsumer consumer = createBlockingQueueConsumer(); this.consumers.put(consumer, true); AsyncMessageProcessingConsumer processor = new AsyncMessageProcessingConsumer(consumer); this.taskExecutor.execute(processor); try { FatalListenerStartupException startupException = processor.getStartupException(); if (startupException != null) { this.consumers.remove(consumer); throw new AmqpIllegalStateException( "Fatal exception on listener startup", startupException); } } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } catch (Exception e) { consumer.stop(); logger.error("Error starting new consumer", e); consumers.remove(consumer); } } } } }
@Override protected void doShutdown() { if (!this.isRunning()) { return; } try { synchronized (consumersMonitor) { if (this.consumers != null) { for (BlockingQueueConsumer consumer : this.consumers.keySet()) { consumer.setQuiesce(this.shutdownTimeout); } } } logger.info("Waiting for workers to finish."); boolean finished = cancellationLock.await(shutdownTimeout, TimeUnit.MILLISECONDS); if (finished) { logger.info("Successfully waited for workers to finish."); } else { logger.info("Workers not finished. Forcing connections to close."); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Interrupted waiting for workers. Continuing with shutdown."); } synchronized (this.consumersMonitor) { this.consumers = null; } }
/** * Specify the number of concurrent consumers to create. Default is 1. * * <p>Raising the number of concurrent consumers is recommended in order to scale the consumption * of messages coming in from a queue. However, note that any ordering guarantees are lost once * multiple consumers are registered. In general, stick with 1 consumer for low-volume queues. * Cannot be less than {@link #maxConcurrentConsumers} (if set). * * @see #setMaxConcurrentConsumers(int) * @param concurrentConsumers the minimum number of consumers to create. */ public void setConcurrentConsumers(final int concurrentConsumers) { Assert.isTrue(concurrentConsumers > 0, "'concurrentConsumers' value must be at least 1 (one)"); if (this.maxConcurrentConsumers != null) { Assert.isTrue( concurrentConsumers <= this.maxConcurrentConsumers, "'concurrentConsumers' cannot be more than 'maxConcurrentConsumers'"); } synchronized (consumersMonitor) { if (logger.isDebugEnabled()) { logger.debug( "Changing consumers from " + this.concurrentConsumers + " to " + concurrentConsumers); } int delta = this.concurrentConsumers - concurrentConsumers; this.concurrentConsumers = concurrentConsumers; if (isActive() && this.consumers != null) { if (delta > 0) { Iterator<Entry<BlockingQueueConsumer, Boolean>> entryIterator = consumers.entrySet().iterator(); while (entryIterator.hasNext() && delta > 0) { Entry<BlockingQueueConsumer, Boolean> entry = entryIterator.next(); if (entry.getValue()) { BlockingQueueConsumer consumer = entry.getKey(); consumer.setQuiesce(this.shutdownTimeout); this.consumers.put(consumer, false); delta--; } } } else { addAndStartConsumers(-delta); } } } }
private boolean doReceiveAndExecute(BlockingQueueConsumer consumer) throws Throwable { Channel channel = consumer.getChannel(); for (int i = 0; i < txSize; i++) { logger.trace("Waiting for message from consumer."); Message message = consumer.nextMessage(receiveTimeout); if (message == null) { break; } try { executeListener(channel, message); } catch (ImmediateAcknowledgeAmqpException e) { break; } catch (Throwable ex) { consumer.rollbackOnExceptionIfNecessary(ex); throw ex; } } return consumer.commitIfNecessary(isChannelLocallyTransacted(channel)); }
private void restart(BlockingQueueConsumer consumer) { synchronized (this.consumersMonitor) { if (this.consumers != null) { try { // Need to recycle the channel in this consumer consumer.stop(); // Ensure consumer counts are correct (another is going // to start because of the exception, but // we haven't counted down yet) this.cancellationLock.release(consumer); this.consumers.remove(consumer); consumer = createBlockingQueueConsumer(); this.consumers.add(consumer); } catch (RuntimeException e) { logger.warn( "Consumer failed irretrievably on restart. " + e.getClass() + ": " + e.getMessage()); // Re-throw and have it logged properly by the caller. throw e; } this.taskExecutor.execute(new AsyncMessageProcessingConsumer(consumer)); } } }
private void restart(BlockingQueueConsumer consumer) { synchronized (this.consumersMonitor) { if (this.consumers != null) { try { // Need to recycle the channel in this consumer consumer.stop(); // Ensure consumer counts are correct (another is going // to start because of the exception, but // we haven't counted down yet) this.cancellationLock.release(consumer); this.consumers.remove(consumer); consumer = createBlockingQueueConsumer(); this.consumers.add(consumer); } catch (RuntimeException e) { logger.warn("Consumer died on restart. " + e.getClass() + ": " + e.getMessage()); // Thrown into the void (probably) in a background thread. // Oh well, here goes... throw e; } this.taskExecutor.execute(new AsyncMessageProcessingConsumer(consumer)); } } }
public void run() { boolean aborted = false; try { try { consumer.start(); start.countDown(); } catch (FatalListenerStartupException ex) { throw ex; } catch (Throwable t) { start.countDown(); handleStartupFailure(t); throw t; } // Always better to stop receiving as soon as possible if // transactional boolean continuable = false; while (isActive() || continuable) { try { // Will come back false when the queue is drained continuable = receiveAndExecute(consumer) && !isChannelTransacted(); } catch (ListenerExecutionFailedException ex) { // Continue to process, otherwise re-throw } } } catch (InterruptedException e) { logger.debug("Consumer thread interrupted, processing stopped."); Thread.currentThread().interrupt(); aborted = true; } catch (FatalListenerStartupException ex) { logger.error("Consumer received fatal exception on startup", ex); this.startupException = ex; // Fatal, but no point re-throwing, so just abort. aborted = true; } catch (FatalListenerExecutionException ex) { logger.error("Consumer received fatal exception during processing", ex); // Fatal, but no point re-throwing, so just abort. aborted = true; } catch (Throwable t) { if (logger.isDebugEnabled()) { logger.warn( "Consumer raised exception, processing can restart if the connection factory supports it", t); } else { logger.warn( "Consumer raised exception, processing can restart if the connection factory supports it. " + "Exception summary: " + t); } } // In all cases count down to allow container to progress beyond startup start.countDown(); if (!isActive() || aborted) { logger.debug("Cancelling " + consumer); try { consumer.stop(); } catch (AmqpException e) { logger.info("Could not cancel message consumer", e); } if (aborted) { logger.info("Stopping container from aborted consumer"); stop(); } } else { logger.info("Restarting " + consumer); restart(consumer); } }
@Override public void run() { boolean aborted = false; int consecutiveIdles = 0; int consecutiveMessages = 0; try { try { this.consumer.start(); this.start.countDown(); } catch (FatalListenerStartupException ex) { throw ex; } catch (Throwable t) { this.start.countDown(); handleStartupFailure(t); throw t; } if (SimpleMessageListenerContainer.this.transactionManager != null) { /* * Register the consumer's channel so it will be used by the transaction manager * if it's an instance of RabbitTransactionManager. */ ConsumerChannelRegistry.registerConsumerChannel( consumer.getChannel(), getConnectionFactory()); } // Always better to stop receiving as soon as possible if // transactional boolean continuable = false; while (isActive(this.consumer) || continuable) { try { // Will come back false when the queue is drained continuable = receiveAndExecute(this.consumer) && !isChannelTransacted(); if (SimpleMessageListenerContainer.this.maxConcurrentConsumers != null) { if (continuable) { consecutiveIdles = 0; if (consecutiveMessages++ > SimpleMessageListenerContainer.this.consecutiveActiveTrigger) { considerAddingAConsumer(); consecutiveMessages = 0; } } else { consecutiveMessages = 0; if (consecutiveIdles++ > SimpleMessageListenerContainer.this.consecutiveIdleTrigger) { considerStoppingAConsumer(this.consumer); consecutiveIdles = 0; } } } } catch (ListenerExecutionFailedException ex) { // Continue to process, otherwise re-throw } } } catch (InterruptedException e) { logger.debug("Consumer thread interrupted, processing stopped."); Thread.currentThread().interrupt(); aborted = true; } catch (FatalListenerStartupException ex) { logger.error("Consumer received fatal exception on startup", ex); this.startupException = ex; // Fatal, but no point re-throwing, so just abort. aborted = true; } catch (FatalListenerExecutionException ex) { logger.error("Consumer received fatal exception during processing", ex); // Fatal, but no point re-throwing, so just abort. aborted = true; } catch (ShutdownSignalException e) { Object shutdownReason = e.getReason(); if (shutdownReason instanceof AMQP.Connection.Close && AMQP.REPLY_SUCCESS == ((AMQP.Connection.Close) shutdownReason).getReplyCode() && "OK".equals(((AMQP.Connection.Close) shutdownReason).getReplyText())) { logger.debug("Consumer received Shutdown Signal, processing stopped.", e); } else { this.logConsumerException(e); } } catch (Error e) { logger.error("Consumer thread error, thread abort.", e); aborted = true; } catch (Throwable t) { this.logConsumerException(t); } finally { if (SimpleMessageListenerContainer.this.transactionManager != null) { ConsumerChannelRegistry.unRegisterConsumerChannel(); } } // In all cases count down to allow container to progress beyond startup start.countDown(); if (!isActive(consumer) || aborted) { logger.debug("Cancelling " + this.consumer); try { this.consumer.stop(); synchronized (consumersMonitor) { if (SimpleMessageListenerContainer.this.consumers != null) { SimpleMessageListenerContainer.this.consumers.remove(this.consumer); } } } catch (AmqpException e) { logger.info("Could not cancel message consumer", e); } if (aborted) { logger.info("Stopping container from aborted consumer"); stop(); } } else { logger.info("Restarting " + this.consumer); restart(this.consumer); } }