INT-2285 Implement max-subscribers on Channels
Add a subscriber limit on both unicast and publish-subscribe
channels. This permits detection of inadvertent channel wiring
during context initialization.
Also add a mechanism to globally set these defaults.
Two properties are added to ChannelInitializer. If the
'channelInitializer' bean is declared before a channel, and
these properties are set, it will globally override the
default (Integer.MAX_VALUE) for these properties.
For example:
<bean id="channelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="true" />
<property name="defaultMaxUnicastSubscribers" value="1" />
<property name="defaultMaxMulticastSubscribers" value="2" />
</bean>
will make the default max-subscribers 1 and 2 for <channel/> and
<publish-subscribe/> channels respectively.
Also applies to module channels (jms, amqp, redis).
This commit is contained in:
committed by
Gunnar Hillert
parent
a90a7e8e0b
commit
2ecb14de2a
@@ -20,7 +20,6 @@ import javax.jms.MessageListener;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.Message;
|
||||
@@ -29,6 +28,7 @@ import org.springframework.integration.MessageDispatchingException;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.dispatcher.AbstractDispatcher;
|
||||
import org.springframework.integration.dispatcher.BroadcastingDispatcher;
|
||||
import org.springframework.integration.dispatcher.MessageDispatcher;
|
||||
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
|
||||
@@ -48,16 +48,26 @@ public class SubscribableJmsChannel extends AbstractJmsChannel implements Subscr
|
||||
|
||||
private final AbstractMessageListenerContainer container;
|
||||
|
||||
private volatile MessageDispatcher dispatcher;
|
||||
|
||||
private volatile AbstractDispatcher dispatcher;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
|
||||
private volatile int maxSubscribers = Integer.MAX_VALUE;
|
||||
|
||||
public SubscribableJmsChannel(AbstractMessageListenerContainer container, JmsTemplate jmsTemplate) {
|
||||
super(jmsTemplate);
|
||||
Assert.notNull(container, "container must not be null");
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the maximum number of subscribers supported by the
|
||||
* channel's dispatcher.
|
||||
* @param maxSubscribers
|
||||
*/
|
||||
public void setMaxSubscribers(int maxSubscribers) {
|
||||
this.maxSubscribers = maxSubscribers;
|
||||
}
|
||||
|
||||
public boolean subscribe(MessageHandler handler) {
|
||||
Assert.state(this.dispatcher != null, "'MessageDispatcher' must not be null. This channel might not have been initialized");
|
||||
@@ -96,6 +106,7 @@ public class SubscribableJmsChannel extends AbstractJmsChannel implements Subscr
|
||||
unicastingDispatcher.setLoadBalancingStrategy(new RoundRobinLoadBalancingStrategy());
|
||||
this.dispatcher = unicastingDispatcher;
|
||||
}
|
||||
this.dispatcher.setMaxSubscribers(this.maxSubscribers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
private volatile String clientId;
|
||||
|
||||
private volatile String concurrency;
|
||||
|
||||
|
||||
private volatile Integer concurrentConsumers;
|
||||
|
||||
private volatile ConnectionFactory connectionFactory;
|
||||
@@ -110,7 +110,7 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
private volatile Long receiveTimeout;
|
||||
|
||||
private volatile Long recoveryInterval;
|
||||
|
||||
|
||||
private volatile String beanName;
|
||||
|
||||
/**
|
||||
@@ -133,6 +133,8 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
|
||||
private volatile Integer transactionTimeout;
|
||||
|
||||
private volatile int maxSubscribers = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
public JmsChannelFactoryBean() {
|
||||
this(true);
|
||||
@@ -202,7 +204,7 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
public void setConcurrency(String concurrency) {
|
||||
this.concurrency = concurrency;
|
||||
}
|
||||
|
||||
|
||||
public void setConcurrentConsumers(int concurrentConsumers) {
|
||||
this.concurrentConsumers = concurrentConsumers;
|
||||
}
|
||||
@@ -314,6 +316,10 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
this.transactionTimeout = transactionTimeout;
|
||||
}
|
||||
|
||||
public void setMaxSubscribers(int maxSubscribers) {
|
||||
this.maxSubscribers = maxSubscribers;
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
@@ -328,7 +334,9 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
this.initializeJmsTemplate();
|
||||
if (this.messageDriven) {
|
||||
this.container = this.createContainer();
|
||||
this.channel = new SubscribableJmsChannel(this.container, this.jmsTemplate);
|
||||
SubscribableJmsChannel subscribableJmsChannel = new SubscribableJmsChannel(this.container, this.jmsTemplate);
|
||||
subscribableJmsChannel.setMaxSubscribers(this.maxSubscribers);
|
||||
this.channel = subscribableJmsChannel;
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(!Boolean.TRUE.equals(this.pubSubDomain),
|
||||
@@ -388,27 +396,27 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
container.setSessionAcknowledgeMode(this.sessionAcknowledgeMode);
|
||||
container.setSessionTransacted(this.sessionTransacted);
|
||||
container.setSubscriptionDurable(this.subscriptionDurable);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (container instanceof DefaultMessageListenerContainer) {
|
||||
DefaultMessageListenerContainer dmlc = (DefaultMessageListenerContainer) container;
|
||||
if (this.cacheLevelName != null) {
|
||||
dmlc.setCacheLevelName(this.cacheLevelName);
|
||||
}
|
||||
|
||||
|
||||
if (StringUtils.hasText(this.concurrency)){
|
||||
dmlc.setConcurrency(this.concurrency);
|
||||
}
|
||||
|
||||
|
||||
if (this.concurrentConsumers != null){
|
||||
dmlc.setConcurrentConsumers(this.concurrentConsumers);
|
||||
}
|
||||
|
||||
|
||||
if (this.maxConcurrentConsumers != null){
|
||||
dmlc.setMaxConcurrentConsumers(this.maxConcurrentConsumers);
|
||||
}
|
||||
|
||||
|
||||
if (this.idleTaskExecutionLimit != null) {
|
||||
dmlc.setIdleTaskExecutionLimit(this.idleTaskExecutionLimit);
|
||||
}
|
||||
@@ -437,11 +445,11 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
if (StringUtils.hasText(this.concurrency)){
|
||||
smlc.setConcurrency(this.concurrency);
|
||||
}
|
||||
|
||||
|
||||
if (this.concurrentConsumers != null){
|
||||
smlc.setConcurrentConsumers(this.concurrentConsumers);
|
||||
}
|
||||
|
||||
|
||||
smlc.setPubSubNoLocal(this.pubSubNoLocal);
|
||||
smlc.setTaskExecutor(this.taskExecutor);
|
||||
}
|
||||
@@ -485,6 +493,7 @@ public class JmsChannelFactoryBean extends AbstractFactoryBean<AbstractJmsChanne
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void destroyInstance(AbstractJmsChannel instance) throws Exception {
|
||||
if (instance instanceof SubscribableJmsChannel) {
|
||||
((SubscribableJmsChannel) this.channel).destroy();
|
||||
|
||||
@@ -58,9 +58,11 @@ public class JmsChannelParser extends AbstractChannelParser {
|
||||
builder.addPropertyReference("connectionFactory", connectionFactory);
|
||||
if ("channel".equals(element.getLocalName())) {
|
||||
this.parseDestination(element, parserContext, builder, "queue");
|
||||
this.setMaxSubscribersProperty(parserContext, builder, element, IntegrationNamespaceUtils.DEFAULT_MAX_UNICAST_SUBSCRIBERS_PROPERTY_NAME);
|
||||
}
|
||||
else if ("publish-subscribe-channel".equals(element.getLocalName())) {
|
||||
this.parseDestination(element, parserContext, builder, "topic");
|
||||
this.setMaxSubscribersProperty(parserContext, builder, element, IntegrationNamespaceUtils.DEFAULT_MAX_BROADCAST_SUBSCRIBERS_PROPERTY_NAME);
|
||||
}
|
||||
String containerType = element.getAttribute(CONTAINER_TYPE_ATTRIBUTE);
|
||||
String containerClass = element.getAttribute(CONTAINER_CLASS_ATTRIBUTE);
|
||||
|
||||
@@ -376,6 +376,7 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:subscribersAttributeGroup" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="message-driven-channel-adapter">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
time-to-live="123"
|
||||
priority="12"/>
|
||||
|
||||
<jms:channel id="queueNameChannel" queue-name="test.queue"/>
|
||||
<jms:channel id="queueNameChannel" queue-name="test.queue" max-subscribers="1" />
|
||||
|
||||
<jms:channel id="queueNameWithResolverChannel" queue-name="foo"
|
||||
destination-resolver="destinationResolver" connection-factory="connFact"/>
|
||||
|
||||
@@ -131,6 +131,8 @@ public class JmsChannelParserTests {
|
||||
assertEquals(DeliveryMode.PERSISTENT, TestUtils.getPropertyValue(jmsTemplate, "deliveryMode"));
|
||||
assertEquals(123L, TestUtils.getPropertyValue(jmsTemplate, "timeToLive"));
|
||||
assertEquals(12, TestUtils.getPropertyValue(jmsTemplate, "priority"));
|
||||
assertEquals(Integer.MAX_VALUE, TestUtils.getPropertyValue(
|
||||
TestUtils.getPropertyValue(channel, "dispatcher"), "maxSubscribers", Integer.class).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,6 +144,8 @@ public class JmsChannelParserTests {
|
||||
AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container");
|
||||
assertEquals("test.queue", jmsTemplate.getDefaultDestinationName());
|
||||
assertEquals("test.queue", container.getDestinationName());
|
||||
assertEquals(1, TestUtils.getPropertyValue(
|
||||
TestUtils.getPropertyValue(channel, "dispatcher"), "maxSubscribers", Integer.class).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user