GH-58, 80: Exclusive Consumer and Lazy Queues

Resolves #58
Resolves #80

Add properties to support exclusive consumers and lazy queues.
This commit is contained in:
Gary Russell
2017-07-17 11:01:24 -04:00
committed by Artem Bilan
parent 58df5c12e8
commit 5c2a764a2f
6 changed files with 89 additions and 8 deletions

View File

@@ -149,6 +149,16 @@ public abstract class RabbitCommonProperties {
*/
private String prefix = "";
/**
* True if the queue is provisioned as a lazy queue.
*/
private boolean lazy;
/**
* True if the DLQ is provisioned as a lazy queue.
*/
private boolean dlqLazy;
public String getExchangeType() {
return this.exchangeType;
}
@@ -342,4 +352,20 @@ public abstract class RabbitCommonProperties {
this.prefix = prefix;
}
public boolean isLazy() {
return this.lazy;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
public boolean isDlqLazy() {
return this.dlqLazy;
}
public void setDlqLazy(boolean dlqLazy) {
this.dlqLazy = dlqLazy;
}
}

View File

@@ -50,6 +50,11 @@ public class RabbitConsumerProperties extends RabbitCommonProperties {
private long recoveryInterval = 5000;
/**
* True if the consumer is exclusive.
*/
private boolean exclusive;
public boolean isTransacted() {
return transacted;
}
@@ -159,4 +164,13 @@ public class RabbitConsumerProperties extends RabbitCommonProperties {
public void setRecoveryInterval(long recoveryInterval) {
this.recoveryInterval = recoveryInterval;
}
public boolean isExclusive() {
return this.exclusive;
}
public void setExclusive(boolean exclusive) {
this.exclusive = exclusive;
}
}

View File

@@ -349,7 +349,7 @@ public class RabbitExchangeQueueProvisioner implements ProvisioningProvider<Exte
args.put("x-dead-letter-routing-key", dlRk);
}
additionalArgs(args, properties.getExpires(), properties.getMaxLength(), properties.getMaxLengthBytes(),
properties.getMaxPriority(), properties.getTtl());
properties.getMaxPriority(), properties.getTtl(), properties.isLazy());
}
else {
if (properties.getDlqDeadLetterExchange() != null) {
@@ -359,13 +359,14 @@ public class RabbitExchangeQueueProvisioner implements ProvisioningProvider<Exte
args.put("x-dead-letter-routing-key", properties.getDlqDeadLetterRoutingKey());
}
additionalArgs(args, properties.getDlqExpires(), properties.getDlqMaxLength(),
properties.getDlqMaxLengthBytes(), properties.getDlqMaxPriority(), properties.getDlqTtl());
properties.getDlqMaxLengthBytes(), properties.getDlqMaxPriority(), properties.getDlqTtl(),
properties.isDlqLazy());
}
return args;
}
private void additionalArgs(Map<String, Object> args, Integer expires, Integer maxLength, Integer maxLengthBytes,
Integer maxPriority, Integer ttl) {
Integer maxPriority, Integer ttl, boolean lazy) {
if (expires != null) {
args.put("x-expires", expires);
}
@@ -381,6 +382,9 @@ public class RabbitExchangeQueueProvisioner implements ProvisioningProvider<Exte
if (ttl != null) {
args.put("x-message-ttl", ttl);
}
if (lazy) {
args.put("x-queue-mode", "lazy");
}
}

View File

@@ -148,6 +148,12 @@ dlqExpires::
how long before an unused dead letter queue is deleted (ms)
+
Default: `no expiration`
dlqLazy::
Declare the dead letter queue with the `x-queue-mode=lazy` argument.
See https://www.rabbitmq.com/lazy-queues.html[Lazy Queues].
Consider using a policy instead of this setting because using a policy allows changing the setting without deleting the queue.
+
Default: `false`.
dlqMaxLength::
maximum number of messages in the dead letter queue
+
@@ -181,6 +187,11 @@ exchangeType::
The exchange type; `direct`, `fanout` or `topic` for non-partitioned destinations; `direct` or `topic` for partitioned destinations.
+
Default: `topic`.
exclusive::
Create an exclusive consumer; concurrency should be 1 when this is `true`; often used when strict ordering is required but enabling a hot standby instance to take over after a failure.
See `recoveryInterval`, which controls how often a standby instance will attempt to consume.
+
Default: `false`.
expires::
how long before an unused queue is deleted (ms)
+
@@ -189,6 +200,12 @@ headerPatterns::
Patterns for headers to be mapped from inbound messages.
+
Default: `['*']` (all headers).
lazy::
Declare the queue with the `x-queue-mode=lazy` argument.
See https://www.rabbitmq.com/lazy-queues.html[Lazy Queues].
Consider using a policy instead of this setting because using a policy allows changing the setting without deleting the queue.
+
Default: `false`.
maxConcurrency::
the maximum number of consumers
+
@@ -327,6 +344,12 @@ dlqExpires::
Only applies if `requiredGroups` are provided and then only to those groups.
+
Default: `no expiration`
dlqLazy::
Declare the dead letter queue with the `x-queue-mode=lazy` argument.
See https://www.rabbitmq.com/lazy-queues.html[Lazy Queues].
Consider using a policy instead of this setting because using a policy allows changing the setting without deleting the queue.
Only applies if `requiredGroups` are provided and then only to those groups.
+
dlqMaxLength::
maximum number of messages in the dead letter queue
Only applies if `requiredGroups` are provided and then only to those groups.
@@ -368,6 +391,13 @@ headerPatterns::
Patterns for headers to be mapped to outbound messages.
+
Default: `['*']` (all headers).
lazy::
Declare the queue with the `x-queue-mode=lazy` argument.
See https://www.rabbitmq.com/lazy-queues.html[Lazy Queues].
Consider using a policy instead of this setting because using a policy allows changing the setting without deleting the queue.
Only applies if `requiredGroups` are provided and then only to those groups.
+
Default: `false`.
maxLength::
maximum number of messages in the queue
Only applies if `requiredGroups` are provided and then only to those groups.

View File

@@ -263,6 +263,7 @@ public class RabbitMessageChannelBinder
listenerContainer.setQueueNames(consumerDestination.getName());
listenerContainer.setAfterReceivePostProcessors(this.decompressingPostProcessor);
listenerContainer.setMessagePropertiesConverter(RabbitMessageChannelBinder.inboundMessagePropertiesConverter);
listenerContainer.setExclusive(properties.getExtension().isExclusive());
listenerContainer.afterPropertiesSet();
AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer);

View File

@@ -16,11 +16,6 @@
package org.springframework.cloud.stream.binder.rabbit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -85,6 +80,11 @@ import org.springframework.retry.support.RetryTemplate;
import com.rabbitmq.http.client.domain.QueueInfo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Mark Fisher
* @author Gary Russell
@@ -164,6 +164,7 @@ public class RabbitBinderTests extends
ExtendedConsumerProperties<RabbitConsumerProperties> properties = createConsumerProperties();
properties.getExtension().setRequeueRejected(true);
properties.getExtension().setTransacted(true);
properties.getExtension().setExclusive(true);
Binding<MessageChannel> consumerBinding = binder.bindConsumer("props.0", null,
createBindableChannel("input", new BindingProperties()), properties);
Lifecycle endpoint = extractEndpoint(consumerBinding);
@@ -172,6 +173,7 @@ public class RabbitBinderTests extends
assertThat(container.getAcknowledgeMode()).isEqualTo(AcknowledgeMode.AUTO);
assertThat(container.getQueueNames()[0]).startsWith(properties.getExtension().getPrefix());
assertThat(TestUtils.getPropertyValue(container, "transactional", Boolean.class)).isTrue();
assertThat(TestUtils.getPropertyValue(container, "exclusive", Boolean.class)).isTrue();
assertThat(TestUtils.getPropertyValue(container, "concurrentConsumers")).isEqualTo(1);
assertThat(TestUtils.getPropertyValue(container, "maxConcurrentConsumers")).isNull();
assertThat(TestUtils.getPropertyValue(container, "defaultRequeueRejected", Boolean.class)).isTrue();
@@ -291,6 +293,7 @@ public class RabbitBinderTests extends
extProps.setExchangeAutoDelete(true);
extProps.setBindingRoutingKey("foo");
extProps.setExpires(30_000);
extProps.setLazy(true);
extProps.setMaxLength(10_000);
extProps.setMaxLengthBytes(100_000);
extProps.setMaxPriority(10);
@@ -302,6 +305,7 @@ public class RabbitBinderTests extends
extProps.setDlqDeadLetterExchange("propsUser3");
extProps.setDlqDeadLetterRoutingKey("propsUser3");
extProps.setDlqExpires(60_000);
extProps.setDlqLazy(true);
extProps.setDlqMaxLength(20_000);
extProps.setDlqMaxLengthBytes(40_000);
extProps.setDlqMaxPriority(8);
@@ -353,6 +357,7 @@ public class RabbitBinderTests extends
assertThat(args.get("x-message-ttl")).isEqualTo(2_000);
assertThat(args.get("x-dead-letter-exchange")).isEqualTo("customDLX");
assertThat(args.get("x-dead-letter-routing-key")).isEqualTo("customDLRK");
assertThat(args.get("x-queue-mode")).isEqualTo("lazy");
queue = rmt.getClient().getQueue("/", "customDLQ");
@@ -370,6 +375,7 @@ public class RabbitBinderTests extends
assertThat(args.get("x-message-ttl")).isEqualTo(1_000);
assertThat(args.get("x-dead-letter-exchange")).isEqualTo("propsUser3");
assertThat(args.get("x-dead-letter-routing-key")).isEqualTo("propsUser3");
assertThat(args.get("x-queue-mode")).isEqualTo("lazy");
}
@Test