container = this.directReplyToContainers.get(connectionFactory); if (container == null) { container = new DirectReplyToMessageListenerContainer(connectionFactory); container.setMessageListener(this); container.setBeanName(this.beanName + "#" + this.containerInstance.getAndIncrement()); if (this.taskExecutor != null) { container.setTaskExecutor(this.taskExecutor); container.setAfterReceivePostProcessors(this.afterReceivePostProcessors .toArray(new MessagePostProcessor[this.afterReceivePostProcessors.size()])); container.setNoLocal(this.noLocalReplyConsumer); if (this.replyErrorHandler != null) { container.setErrorHandler(this.replyErrorHandler); container.start(); this.directReplyToContainers.put(connectionFactory, container); this.replyAddress = Address.AMQ_RABBITMQ_REPLY_TO; ChannelHolder channelHolder = container.getChannelHolder(); try { Channel channel = channelHolder.getChannel(); container.releaseConsumerFor(channelHolder, false, null);
@Override protected void processMonitorTask() { long now = System.currentTimeMillis(); synchronized (this.consumersMonitor) { long reduce = this.consumers.stream() .filter(c -> this.whenUsed.containsKey(c) && !this.inUseConsumerChannels.containsValue(c) && this.whenUsed.get(c) < now - getIdleEventInterval()) .count(); if (reduce > 0) { if (logger.isDebugEnabled()) { logger.debug("Reducing idle consumes by " + reduce); } this.consumerCount = (int) Math.max(0, this.consumerCount - reduce); super.setConsumersPerQueue(this.consumerCount); } } }
@Override protected void doStart() { if (!isRunning()) { this.consumerCount = 0; super.setConsumersPerQueue(0); super.doStart(); } }
/** * Construct an instance using the provided arguments. "Direct replyTo" is used for * replies. * @param template a {@link RabbitTemplate} * @since 2.0 */ public AsyncRabbitTemplate(RabbitTemplate template) { Assert.notNull(template, "'template' cannot be null"); this.template = template; this.container = null; this.replyAddress = null; this.directReplyToContainer = new DirectReplyToMessageListenerContainer(this.template.getConnectionFactory()); this.directReplyToContainer.setMessageListener(this); }
@Test public void testReplyToReleaseWithCancel() throws Exception { CachingConnectionFactory cf = new CachingConnectionFactory("localhost"); DirectReplyToMessageListenerContainer container = new DirectReplyToMessageListenerContainer(cf); container.setBeanName("releaseCancel"); final CountDownLatch consumeLatch = new CountDownLatch(1); final CountDownLatch releaseLatch = new CountDownLatch(1); container.setApplicationEventPublisher(e -> { if (e instanceof ListenerContainerConsumerTerminatedEvent) { releaseLatch.countDown(); } else if (e instanceof ConsumeOkEvent) { consumeLatch.countDown(); } }); container.afterPropertiesSet(); container.start(); ChannelHolder channelHolder = container.getChannelHolder(); assertTrue(consumeLatch.await(10, TimeUnit.SECONDS)); container.releaseConsumerFor(channelHolder, true, "foo"); assertTrue(releaseLatch.await(10, TimeUnit.SECONDS)); container.stop(); cf.destroy(); }
@Test public void testReleaseConsumerRace() throws Exception { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost"); DirectReplyToMessageListenerContainer container = new DirectReplyToMessageListenerContainer(connectionFactory); final CountDownLatch latch = new CountDownLatch(1); container.setMessageListener(m -> { }); .setPropertyValue("messageListener", mockMessageListener); container.start(); ChannelHolder channel1 = container.getChannelHolder(); BasicProperties props = new BasicProperties().builder().replyTo(Address.AMQ_RABBITMQ_REPLY_TO).build(); channel1.getChannel().basicPublish("", TEST_RELEASE_CONSUMER_Q, props, "foo".getBytes()); assertTrue(latch.await(10, TimeUnit.SECONDS)); ChannelHolder channel2 = container.getChannelHolder(); assertSame(channel1.getChannel(), channel2.getChannel()); container.releaseConsumerFor(channel1, false, null); // simulate race for future timeout/cancel and onMessage() Map<?, ?> inUse = TestUtils.getPropertyValue(container, "inUseConsumerChannels", Map.class); assertThat(inUse.size(), equalTo(1)); container.releaseConsumerFor(channel2, false, null); assertThat(inUse.size(), equalTo(0)); container.stop(); connectionFactory.destroy();
@Override public synchronized void start() { if (!this.running) { if (this.internalTaskScheduler) { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setThreadNamePrefix(getBeanName() == null ? "asyncTemplate-" : (getBeanName() + "-")); scheduler.afterPropertiesSet(); this.taskScheduler = scheduler; } if (this.container != null) { this.container.start(); } if (this.directReplyToContainer != null) { this.directReplyToContainer.setTaskScheduler(this.taskScheduler); this.directReplyToContainer.start(); } } this.running = true; }
@Override public RabbitMessageFuture sendAndReceive(String exchange, String routingKey, Message message) { String correlationId = getOrSetCorrelationIdAndSetReplyTo(message); RabbitMessageFuture future = new RabbitMessageFuture(correlationId, message); CorrelationData correlationData = null; if (this.enableConfirms) { correlationData = new CorrelationData(correlationId); future.setConfirm(new SettableListenableFuture<>()); } this.pending.put(correlationId, future); if (this.container != null) { this.template.send(exchange, routingKey, message, correlationData); } else { ChannelHolder channelHolder = this.directReplyToContainer.getChannelHolder(); future.setChannelHolder(channelHolder); sendDirect(channelHolder.getChannel(), exchange, routingKey, message, correlationData); } future.startTimer(); return future; }
@Override public boolean cancel(boolean mayInterruptIfRunning) { if (this.timeoutTask != null) { this.timeoutTask.cancel(true); } AsyncRabbitTemplate.this.pending.remove(this.correlationId); if (this.channelHolder != null && AsyncRabbitTemplate.this.directReplyToContainer != null) { AsyncRabbitTemplate.this.directReplyToContainer.releaseConsumerFor(this.channelHolder, false, null); // NOSONAR } return super.cancel(mayInterruptIfRunning); }
@Override @SuppressWarnings("deprecation") @Deprecated public void setChannelAwareMessageListener(ChannelAwareMessageListener messageListener) { setMessageListener(messageListener); }
@Override public synchronized void stop() { if (this.running) { if (this.container != null) { this.container.stop(); } if (this.directReplyToContainer != null) { this.directReplyToContainer.stop(); } for (RabbitFuture<?> future : this.pending.values()) { future.setNackCause("AsyncRabbitTemplate was stopped while waiting for reply"); future.cancel(true); } if (this.internalTaskScheduler) { ((ThreadPoolTaskScheduler) this.taskScheduler).destroy(); this.taskScheduler = null; } } this.running = false; }
public DirectReplyToMessageListenerContainer(ConnectionFactory connectionFactory) { super(connectionFactory); super.setQueueNames(Address.AMQ_RABBITMQ_REPLY_TO); setAcknowledgeMode(AcknowledgeMode.NONE); super.setConsumersPerQueue(0); super.setIdleEventInterval(DEFAULT_IDLE); }
@Test public void testReplyToConsumersReduced() throws Exception { CachingConnectionFactory cf = new CachingConnectionFactory("localhost"); DirectReplyToMessageListenerContainer container = new DirectReplyToMessageListenerContainer(cf); container.setBeanName("reducing"); container.setIdleEventInterval(100); CountDownLatch latch = new CountDownLatch(5); container.setApplicationEventPublisher(e -> { if (e instanceof ListenerContainerIdleEvent) { latch.countDown(); } }); container.afterPropertiesSet(); container.start(); ChannelHolder channelHolder1 = container.getChannelHolder(); ChannelHolder channelHolder2 = container.getChannelHolder(); assertTrue(activeConsumerCount(container, 2)); container.releaseConsumerFor(channelHolder2, false, null); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertTrue(channelHolder1.getChannel().isOpen()); container.releaseConsumerFor(channelHolder1, false, null); assertTrue(activeConsumerCount(container, 0)); container.stop(); cf.destroy(); }
/** * Construct an instance using the provided arguments. "Direct replyTo" is used for * replies. * @param template a {@link RabbitTemplate} * @since 2.0 */ public AsyncRabbitTemplate(RabbitTemplate template) { Assert.notNull(template, "'template' cannot be null"); this.template = template; this.container = null; this.replyAddress = null; this.directReplyToContainer = new DirectReplyToMessageListenerContainer(this.template.getConnectionFactory()); this.directReplyToContainer.setMessageListener(this); }
@Override public synchronized void start() { if (!this.running) { if (this.internalTaskScheduler) { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setThreadNamePrefix(getBeanName() == null ? "asyncTemplate-" : (getBeanName() + "-")); scheduler.afterPropertiesSet(); this.taskScheduler = scheduler; } if (this.container != null) { this.container.start(); } if (this.directReplyToContainer != null) { this.directReplyToContainer.setTaskScheduler(this.taskScheduler); this.directReplyToContainer.start(); } } this.running = true; }
@Override public RabbitMessageFuture sendAndReceive(String exchange, String routingKey, Message message) { String correlationId = getOrSetCorrelationIdAndSetReplyTo(message); RabbitMessageFuture future = new RabbitMessageFuture(correlationId, message); CorrelationData correlationData = null; if (this.enableConfirms) { correlationData = new CorrelationData(correlationId); future.setConfirm(new SettableListenableFuture<>()); } this.pending.put(correlationId, future); if (this.container != null) { this.template.send(exchange, routingKey, message, correlationData); } else { ChannelHolder channelHolder = this.directReplyToContainer.getChannelHolder(); future.setChannelHolder(channelHolder); sendDirect(channelHolder.getChannel(), exchange, routingKey, message, correlationData); } future.startTimer(); return future; }
@Override public boolean cancel(boolean mayInterruptIfRunning) { if (this.timeoutTask != null) { this.timeoutTask.cancel(true); } AsyncRabbitTemplate.this.pending.remove(this.correlationId); if (this.channelHolder != null && AsyncRabbitTemplate.this.directReplyToContainer != null) { AsyncRabbitTemplate.this.directReplyToContainer.releaseConsumerFor(this.channelHolder, false, null); // NOSONAR } return super.cancel(mayInterruptIfRunning); }
@Override @SuppressWarnings("deprecation") @Deprecated public void setChannelAwareMessageListener(ChannelAwareMessageListener messageListener) { setMessageListener(messageListener); }
@Override public synchronized void stop() { if (this.running) { if (this.container != null) { this.container.stop(); } if (this.directReplyToContainer != null) { this.directReplyToContainer.stop(); } for (RabbitFuture<?> future : this.pending.values()) { future.setNackCause("AsyncRabbitTemplate was stopped while waiting for reply"); future.cancel(true); } if (this.internalTaskScheduler) { ((ThreadPoolTaskScheduler) this.taskScheduler).destroy(); this.taskScheduler = null; } } this.running = false; }
public DirectReplyToMessageListenerContainer(ConnectionFactory connectionFactory) { super(connectionFactory); super.setQueueNames(Address.AMQ_RABBITMQ_REPLY_TO); setAcknowledgeMode(AcknowledgeMode.NONE); super.setConsumersPerQueue(0); super.setIdleEventInterval(DEFAULT_IDLE); }