TaskExecutorRegistration does not apply its default settings to a user-provided executor

Also, ChannelRegistration.setInterceptors is deprecated now: in favor of a fluently named interceptors(...) method which is documented to add the given interceptors to the channel's current list.

Issue: SPR-15962
Issue: SPR-15976
This commit is contained in:
Juergen Hoeller 2017-09-19 13:47:43 +02:00
parent 5bdcb895c0
commit ac9cfefaff
4 changed files with 85 additions and 54 deletions

View File

@ -126,13 +126,15 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
public AbstractSubscribableChannel clientInboundChannel() { public AbstractSubscribableChannel clientInboundChannel() {
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientInboundChannelExecutor()); ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientInboundChannelExecutor());
ChannelRegistration reg = getClientInboundChannelRegistration(); ChannelRegistration reg = getClientInboundChannelRegistration();
if (reg.hasInterceptors()) {
channel.setInterceptors(reg.getInterceptors()); channel.setInterceptors(reg.getInterceptors());
}
return channel; return channel;
} }
@Bean @Bean
public ThreadPoolTaskExecutor clientInboundChannelExecutor() { public ThreadPoolTaskExecutor clientInboundChannelExecutor() {
TaskExecutorRegistration reg = getClientInboundChannelRegistration().getOrCreateTaskExecRegistration(); TaskExecutorRegistration reg = getClientInboundChannelRegistration().taskExecutor();
ThreadPoolTaskExecutor executor = reg.getTaskExecutor(); ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
executor.setThreadNamePrefix("clientInboundChannel-"); executor.setThreadNamePrefix("clientInboundChannel-");
return executor; return executor;
@ -142,7 +144,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
if (this.clientInboundChannelRegistration == null) { if (this.clientInboundChannelRegistration == null) {
ChannelRegistration registration = new ChannelRegistration(); ChannelRegistration registration = new ChannelRegistration();
configureClientInboundChannel(registration); configureClientInboundChannel(registration);
registration.setInterceptors(new ImmutableMessageChannelInterceptor()); registration.interceptors(new ImmutableMessageChannelInterceptor());
this.clientInboundChannelRegistration = registration; this.clientInboundChannelRegistration = registration;
} }
return this.clientInboundChannelRegistration; return this.clientInboundChannelRegistration;
@ -159,13 +161,15 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
public AbstractSubscribableChannel clientOutboundChannel() { public AbstractSubscribableChannel clientOutboundChannel() {
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientOutboundChannelExecutor()); ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientOutboundChannelExecutor());
ChannelRegistration reg = getClientOutboundChannelRegistration(); ChannelRegistration reg = getClientOutboundChannelRegistration();
if (reg.hasInterceptors()) {
channel.setInterceptors(reg.getInterceptors()); channel.setInterceptors(reg.getInterceptors());
}
return channel; return channel;
} }
@Bean @Bean
public ThreadPoolTaskExecutor clientOutboundChannelExecutor() { public ThreadPoolTaskExecutor clientOutboundChannelExecutor() {
TaskExecutorRegistration reg = getClientOutboundChannelRegistration().getOrCreateTaskExecRegistration(); TaskExecutorRegistration reg = getClientOutboundChannelRegistration().taskExecutor();
ThreadPoolTaskExecutor executor = reg.getTaskExecutor(); ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
executor.setThreadNamePrefix("clientOutboundChannel-"); executor.setThreadNamePrefix("clientOutboundChannel-");
return executor; return executor;
@ -175,7 +179,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
if (this.clientOutboundChannelRegistration == null) { if (this.clientOutboundChannelRegistration == null) {
ChannelRegistration registration = new ChannelRegistration(); ChannelRegistration registration = new ChannelRegistration();
configureClientOutboundChannel(registration); configureClientOutboundChannel(registration);
registration.setInterceptors(new ImmutableMessageChannelInterceptor()); registration.interceptors(new ImmutableMessageChannelInterceptor());
this.clientOutboundChannelRegistration = registration; this.clientOutboundChannelRegistration = registration;
} }
return this.clientOutboundChannelRegistration; return this.clientOutboundChannelRegistration;
@ -191,9 +195,9 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
@Bean @Bean
public AbstractSubscribableChannel brokerChannel() { public AbstractSubscribableChannel brokerChannel() {
ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration(); ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration();
ExecutorSubscribableChannel channel = reg.hasTaskExecutor() ? ExecutorSubscribableChannel channel = (reg.hasTaskExecutor() ?
new ExecutorSubscribableChannel(brokerChannelExecutor()) : new ExecutorSubscribableChannel(); new ExecutorSubscribableChannel(brokerChannelExecutor()) : new ExecutorSubscribableChannel());
reg.setInterceptors(new ImmutableMessageChannelInterceptor()); reg.interceptors(new ImmutableMessageChannelInterceptor());
channel.setInterceptors(reg.getInterceptors()); channel.setInterceptors(reg.getInterceptors());
return channel; return channel;
} }

View File

@ -43,45 +43,45 @@ public class ChannelRegistration {
* Configure the thread pool backing this message channel. * Configure the thread pool backing this message channel.
*/ */
public TaskExecutorRegistration taskExecutor() { public TaskExecutorRegistration taskExecutor() {
if (this.registration == null) { return taskExecutor(null);
this.registration = new TaskExecutorRegistration();
}
return this.registration;
} }
/** /**
* Configure the thread pool backing this message channel using a custom * Configure the thread pool backing this message channel using a custom
* ThreadPoolTaskExecutor. * ThreadPoolTaskExecutor.
* @param taskExecutor the executor to use (or {@code null} for a default executor)
*/ */
public TaskExecutorRegistration taskExecutor(ThreadPoolTaskExecutor taskExecutor) { public TaskExecutorRegistration taskExecutor(@Nullable ThreadPoolTaskExecutor taskExecutor) {
if (this.registration == null) { if (this.registration == null) {
this.registration = new TaskExecutorRegistration(taskExecutor); this.registration = (taskExecutor != null ? new TaskExecutorRegistration(taskExecutor) :
new TaskExecutorRegistration());
} }
return this.registration; return this.registration;
} }
/** /**
* Configure interceptors for the message channel. * Configure the given interceptors for this message channel,
* adding them to the channel's current list of interceptors.
* @since 4.3.12
*/ */
public ChannelRegistration setInterceptors(ChannelInterceptor... interceptors) { public ChannelRegistration interceptors(ChannelInterceptor... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors)); this.interceptors.addAll(Arrays.asList(interceptors));
return this; return this;
} }
/**
* @deprecated as of 4.3.12, in favor of {@link #interceptors(ChannelInterceptor...)}
*/
@Deprecated
public ChannelRegistration setInterceptors(ChannelInterceptor... interceptors) {
return interceptors(interceptors);
}
protected boolean hasTaskExecutor() { protected boolean hasTaskExecutor() {
return (this.registration != null); return (this.registration != null);
} }
@Nullable
protected TaskExecutorRegistration getTaskExecRegistration() {
return this.registration;
}
protected TaskExecutorRegistration getOrCreateTaskExecRegistration() {
return taskExecutor();
}
protected boolean hasInterceptors() { protected boolean hasInterceptors() {
return !this.interceptors.isEmpty(); return !this.interceptors.isEmpty();
} }
@ -89,4 +89,5 @@ public class ChannelRegistration {
protected List<ChannelInterceptor> getInterceptors() { protected List<ChannelInterceptor> getInterceptors() {
return this.interceptors; return this.interceptors;
} }
} }

View File

@ -18,34 +18,53 @@ package org.springframework.messaging.simp.config;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.Assert;
/** /**
* A registration class for customizing the properties of {@link ThreadPoolTaskExecutor}. * A registration class for customizing the properties of {@link ThreadPoolTaskExecutor}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0 * @since 4.0
*/ */
public class TaskExecutorRegistration { public class TaskExecutorRegistration {
private final ThreadPoolTaskExecutor taskExecutor;
@Nullable @Nullable
private ThreadPoolTaskExecutor taskExecutor; private Integer corePoolSize;
private int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; @Nullable
private Integer maxPoolSize;
private int maxPoolSize = Integer.MAX_VALUE; @Nullable
private Integer keepAliveSeconds;
private int queueCapacity = Integer.MAX_VALUE; @Nullable
private Integer queueCapacity;
private int keepAliveSeconds = 60;
/**
* Create a new {@code TaskExecutorRegistration} for a default
* {@link ThreadPoolTaskExecutor}.
*/
public TaskExecutorRegistration() { public TaskExecutorRegistration() {
this.taskExecutor = new ThreadPoolTaskExecutor();
this.taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
this.taskExecutor.setAllowCoreThreadTimeOut(true);
} }
/**
* Create a new {@code TaskExecutorRegistration} for a given
* {@link ThreadPoolTaskExecutor}.
* @param taskExecutor the executor to use
*/
public TaskExecutorRegistration(ThreadPoolTaskExecutor taskExecutor) { public TaskExecutorRegistration(ThreadPoolTaskExecutor taskExecutor) {
Assert.notNull(taskExecutor, "ThreadPoolTaskExecutor must not be null");
this.taskExecutor = taskExecutor; this.taskExecutor = taskExecutor;
} }
/** /**
* Set the core pool size of the ThreadPoolExecutor. * Set the core pool size of the ThreadPoolExecutor.
* <p><strong>NOTE:</strong> The core pool size is effectively the max pool size * <p><strong>NOTE:</strong> The core pool size is effectively the max pool size
@ -77,6 +96,18 @@ public class TaskExecutorRegistration {
return this; return this;
} }
/**
* Set the time limit for which threads may remain idle before being terminated.
* If there are more than the core number of threads currently in the pool,
* after waiting this amount of time without processing a task, excess threads
* will be terminated. This overrides any value set in the constructor.
* <p>By default this is set to 60.
*/
public TaskExecutorRegistration keepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
return this;
}
/** /**
* Set the queue capacity for the ThreadPoolExecutor. * Set the queue capacity for the ThreadPoolExecutor.
* <p><strong>NOTE:</strong> when an unbounded {@code queueCapacity} is configured * <p><strong>NOTE:</strong> when an unbounded {@code queueCapacity} is configured
@ -91,26 +122,21 @@ public class TaskExecutorRegistration {
return this; return this;
} }
/**
* Set the time limit for which threads may remain idle before being terminated.
* If there are more than the core number of threads currently in the pool,
* after waiting this amount of time without processing a task, excess threads
* will be terminated. This overrides any value set in the constructor.
* <p>By default this is set to 60.
*/
public TaskExecutorRegistration keepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
return this;
}
protected ThreadPoolTaskExecutor getTaskExecutor() { protected ThreadPoolTaskExecutor getTaskExecutor() {
ThreadPoolTaskExecutor executor = (this.taskExecutor != null ? this.taskExecutor : new ThreadPoolTaskExecutor()); if (this.corePoolSize != null) {
executor.setCorePoolSize(this.corePoolSize); this.taskExecutor.setCorePoolSize(this.corePoolSize);
executor.setMaxPoolSize(this.maxPoolSize); }
executor.setKeepAliveSeconds(this.keepAliveSeconds); if (this.maxPoolSize != null) {
executor.setQueueCapacity(this.queueCapacity); this.taskExecutor.setMaxPoolSize(this.maxPoolSize);
executor.setAllowCoreThreadTimeOut(true); }
return executor; if (this.keepAliveSeconds != null) {
this.taskExecutor.setKeepAliveSeconds(this.keepAliveSeconds);
}
if (this.queueCapacity != null) {
this.taskExecutor.setQueueCapacity(this.queueCapacity);
}
return this.taskExecutor;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 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.
@ -512,14 +512,14 @@ public class MessageBrokerConfigurationTests {
@Override @Override
protected void configureClientInboundChannel(ChannelRegistration registration) { protected void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(this.interceptor); registration.interceptors(this.interceptor);
registration.taskExecutor(new CustomThreadPoolTaskExecutor()) registration.taskExecutor(new CustomThreadPoolTaskExecutor())
.corePoolSize(11).maxPoolSize(12).keepAliveSeconds(13).queueCapacity(14); .corePoolSize(11).maxPoolSize(12).keepAliveSeconds(13).queueCapacity(14);
} }
@Override @Override
protected void configureClientOutboundChannel(ChannelRegistration registration) { protected void configureClientOutboundChannel(ChannelRegistration registration) {
registration.setInterceptors(this.interceptor, this.interceptor); registration.interceptors(this.interceptor, this.interceptor);
registration.taskExecutor().corePoolSize(21).maxPoolSize(22).keepAliveSeconds(23).queueCapacity(24); registration.taskExecutor().corePoolSize(21).maxPoolSize(22).keepAliveSeconds(23).queueCapacity(24);
} }
@ -535,7 +535,7 @@ public class MessageBrokerConfigurationTests {
@Override @Override
protected void configureMessageBroker(MessageBrokerRegistry registry) { protected void configureMessageBroker(MessageBrokerRegistry registry) {
registry.configureBrokerChannel().setInterceptors(this.interceptor, this.interceptor, this.interceptor); registry.configureBrokerChannel().interceptors(this.interceptor, this.interceptor, this.interceptor);
registry.configureBrokerChannel().taskExecutor().corePoolSize(31).maxPoolSize(32).keepAliveSeconds(33).queueCapacity(34); registry.configureBrokerChannel().taskExecutor().corePoolSize(31).maxPoolSize(32).keepAliveSeconds(33).queueCapacity(34);
registry.setPathMatcher(new AntPathMatcher(".")).enableSimpleBroker("/topic", "/queue"); registry.setPathMatcher(new AntPathMatcher(".")).enableSimpleBroker("/topic", "/queue");
registry.setCacheLimit(8192); registry.setCacheLimit(8192);