AMQP-810: Fix adjust consumers when max present
JIRA: https://jira.spring.io/browse/AMQP-810 SMLC: adjusting the `concurrentConsumers` did not consider `maxConcurrentConsumers`. - increase added consumers even if at max - decrease removed consumers when they had increased due to max being set Further, decreasing the `maxConcurrentConsumers` did not remove consumers if there were more consumers than the new max. - don't add consumers beyond the max - don't remove consumers unless the new max is exceeded **cherry-pick to 2.0.x, 1.7.x** There will be some minor conflicts in 1.7.x since the modified test is JUnit5.
This commit is contained in:
committed by
Artem Bilan
parent
f678078e29
commit
daebf40a69
@@ -164,19 +164,8 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
|
|||||||
}
|
}
|
||||||
int delta = this.concurrentConsumers - concurrentConsumers;
|
int delta = this.concurrentConsumers - concurrentConsumers;
|
||||||
this.concurrentConsumers = concurrentConsumers;
|
this.concurrentConsumers = concurrentConsumers;
|
||||||
if (isActive() && this.consumers != null) {
|
if (isActive()) {
|
||||||
if (delta > 0) {
|
adjustConsumers(delta);
|
||||||
Iterator<BlockingQueueConsumer> consumerIterator = this.consumers.iterator();
|
|
||||||
while (consumerIterator.hasNext() && delta > 0) {
|
|
||||||
BlockingQueueConsumer consumer = consumerIterator.next();
|
|
||||||
consumer.basicCancel(true);
|
|
||||||
consumerIterator.remove();
|
|
||||||
delta--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
addAndStartConsumers(-delta);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +185,15 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
|
|||||||
"'maxConcurrentConsumers' value must be at least 'concurrentConsumers'");
|
"'maxConcurrentConsumers' value must be at least 'concurrentConsumers'");
|
||||||
Assert.isTrue(!isExclusive() || maxConcurrentConsumers == 1,
|
Assert.isTrue(!isExclusive() || maxConcurrentConsumers == 1,
|
||||||
"When the consumer is exclusive, the concurrency must be 1");
|
"When the consumer is exclusive, the concurrency must be 1");
|
||||||
|
Integer oldMax = this.maxConcurrentConsumers;
|
||||||
this.maxConcurrentConsumers = maxConcurrentConsumers;
|
this.maxConcurrentConsumers = maxConcurrentConsumers;
|
||||||
|
if (oldMax != null && isActive()) {
|
||||||
|
int delta = oldMax - maxConcurrentConsumers;
|
||||||
|
if (delta > 0) { // only decrease, not increase
|
||||||
|
adjustConsumers(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -587,10 +584,45 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust consumers depending on delta.
|
||||||
|
* @param delta a negative value increases, positive decreases.
|
||||||
|
* @since 1.7.8
|
||||||
|
*/
|
||||||
|
protected void adjustConsumers(int delta) {
|
||||||
|
synchronized (this.consumersMonitor) {
|
||||||
|
if (isActive() && this.consumers != null) {
|
||||||
|
if (delta > 0) {
|
||||||
|
Iterator<BlockingQueueConsumer> consumerIterator = this.consumers.iterator();
|
||||||
|
while (consumerIterator.hasNext() && delta > 0
|
||||||
|
&& (this.maxConcurrentConsumers == null
|
||||||
|
|| this.consumers.size() > this.maxConcurrentConsumers)) {
|
||||||
|
BlockingQueueConsumer consumer = consumerIterator.next();
|
||||||
|
consumer.basicCancel(true);
|
||||||
|
consumerIterator.remove();
|
||||||
|
delta--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addAndStartConsumers(-delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start up to delta consumers, limited by {@link #setMaxConcurrentConsumers(int)}.
|
||||||
|
* @param delta the consumers to add.
|
||||||
|
*/
|
||||||
protected void addAndStartConsumers(int delta) {
|
protected void addAndStartConsumers(int delta) {
|
||||||
synchronized (this.consumersMonitor) {
|
synchronized (this.consumersMonitor) {
|
||||||
if (this.consumers != null) {
|
if (this.consumers != null) {
|
||||||
for (int i = 0; i < delta; i++) {
|
for (int i = 0; i < delta; i++) {
|
||||||
|
if (this.maxConcurrentConsumers != null
|
||||||
|
&& this.consumers.size() >= this.maxConcurrentConsumers) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
BlockingQueueConsumer consumer = createBlockingQueueConsumer();
|
BlockingQueueConsumer consumer = createBlockingQueueConsumer();
|
||||||
this.consumers.add(consumer);
|
this.consumers.add(consumer);
|
||||||
AsyncMessageProcessingConsumer processor = new AsyncMessageProcessingConsumer(consumer);
|
AsyncMessageProcessingConsumer processor = new AsyncMessageProcessingConsumer(consumer);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.amqp.rabbit.listener;
|
package org.springframework.amqp.rabbit.listener;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -44,12 +46,23 @@ import com.rabbitmq.client.ConnectionFactory;
|
|||||||
* @since 1.2.1
|
* @since 1.2.1
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@RabbitAvailable(queues = SimpleMessageListenerContainerLongTests.QUEUE)
|
@RabbitAvailable(queues = {
|
||||||
|
SimpleMessageListenerContainerLongTests.QUEUE,
|
||||||
|
SimpleMessageListenerContainerLongTests.QUEUE2,
|
||||||
|
SimpleMessageListenerContainerLongTests.QUEUE3,
|
||||||
|
SimpleMessageListenerContainerLongTests.QUEUE4
|
||||||
|
})
|
||||||
@LongRunning
|
@LongRunning
|
||||||
public class SimpleMessageListenerContainerLongTests {
|
public class SimpleMessageListenerContainerLongTests {
|
||||||
|
|
||||||
public static final String QUEUE = "SimpleMessageListenerContainerLongTests.queue";
|
public static final String QUEUE = "SimpleMessageListenerContainerLongTests.queue";
|
||||||
|
|
||||||
|
public static final String QUEUE2 = "SimpleMessageListenerContainerLongTests.queue2";
|
||||||
|
|
||||||
|
public static final String QUEUE3 = "SimpleMessageListenerContainerLongTests.queue3";
|
||||||
|
|
||||||
|
public static final String QUEUE4 = "SimpleMessageListenerContainerLongTests.queue4";
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(SimpleMessageListenerContainerLongTests.class);
|
private final Log logger = LogFactory.getLog(SimpleMessageListenerContainerLongTests.class);
|
||||||
|
|
||||||
private final SingleConnectionFactory connectionFactory;
|
private final SingleConnectionFactory connectionFactory;
|
||||||
@@ -132,6 +145,90 @@ public class SimpleMessageListenerContainerLongTests {
|
|||||||
connectionFactory.destroy();
|
connectionFactory.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncreaseMinAtMax() throws Exception {
|
||||||
|
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(this.connectionFactory);
|
||||||
|
container.setStartConsumerMinInterval(100);
|
||||||
|
container.setConsecutiveActiveTrigger(1);
|
||||||
|
container.setMessageListener(m -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
container.setQueueNames(QUEUE2);
|
||||||
|
container.setConcurrentConsumers(2);
|
||||||
|
container.setMaxConcurrentConsumers(5);
|
||||||
|
container.afterPropertiesSet();
|
||||||
|
container.start();
|
||||||
|
RabbitTemplate template = new RabbitTemplate(this.connectionFactory);
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
template.convertAndSend(QUEUE2, "foo");
|
||||||
|
}
|
||||||
|
waitForNConsumers(container, 5);
|
||||||
|
container.setConcurrentConsumers(4);
|
||||||
|
Set<?> consumers = (Set<?>) TestUtils.getPropertyValue(container, "consumers");
|
||||||
|
assertThat(consumers.size(), equalTo(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecreaseMinAtMax() throws Exception {
|
||||||
|
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(this.connectionFactory);
|
||||||
|
container.setStartConsumerMinInterval(100);
|
||||||
|
container.setConsecutiveActiveTrigger(1);
|
||||||
|
container.setMessageListener(m -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
container.setQueueNames(QUEUE3);
|
||||||
|
container.setConcurrentConsumers(2);
|
||||||
|
container.setMaxConcurrentConsumers(3);
|
||||||
|
container.afterPropertiesSet();
|
||||||
|
container.start();
|
||||||
|
RabbitTemplate template = new RabbitTemplate(this.connectionFactory);
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
template.convertAndSend(QUEUE3, "foo");
|
||||||
|
}
|
||||||
|
waitForNConsumers(container, 3);
|
||||||
|
container.setConcurrentConsumers(1);
|
||||||
|
Set<?> consumers = (Set<?>) TestUtils.getPropertyValue(container, "consumers");
|
||||||
|
assertThat(consumers.size(), equalTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecreaseMaxAtMax() throws Exception {
|
||||||
|
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(this.connectionFactory);
|
||||||
|
container.setStartConsumerMinInterval(100);
|
||||||
|
container.setConsecutiveActiveTrigger(1);
|
||||||
|
container.setMessageListener(m -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
container.setQueueNames(QUEUE4);
|
||||||
|
container.setConcurrentConsumers(2);
|
||||||
|
container.setMaxConcurrentConsumers(3);
|
||||||
|
container.afterPropertiesSet();
|
||||||
|
container.start();
|
||||||
|
RabbitTemplate template = new RabbitTemplate(this.connectionFactory);
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
template.convertAndSend(QUEUE4, "foo");
|
||||||
|
}
|
||||||
|
waitForNConsumers(container, 3);
|
||||||
|
container.setConcurrentConsumers(1);
|
||||||
|
container.setMaxConcurrentConsumers(1);
|
||||||
|
Set<?> consumers = (Set<?>) TestUtils.getPropertyValue(container, "consumers");
|
||||||
|
assertThat(consumers.size(), equalTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
public void handleMessage(String foo) {
|
public void handleMessage(String foo) {
|
||||||
logger.info(foo);
|
logger.info(foo);
|
||||||
|
|||||||
Reference in New Issue
Block a user