目录
两种MessageChannel实现
TemporaryReplyChannel
- 用于接收单个回复消息的临时通道。在整个断点调试过程中没有追踪到,所以在这里不详细说明。
ExecutorSubscribableChannel
- 正如字面上所表示的这样Executor(线程池)Subscribable(可订阅的)Channel(通道)——一个通过线程池将消息发送给每个订阅者的通道。这也是Spring-Messaging功能的核心,理解了这个实现类的构成,就很容易掌握《Spring Websocket+SockJS+STOMP 实现即时通信》
剖析ExecutorSubscribableChannel
ExecutorSubscribableChannel类,通过继承父类方法或直接声明,可以看作由 六
部分构成
- beanName:主要用作日志记录,用来区分ExecutorSubscribableChannel的不同实例;
- handlers:MessageHandler集合,作为MessageChannel的订阅者,用来处理Messages;
- SendTask :一个内部类,是MessageHandlingRunnable的子类,将一个Message与一个MessageHandler封装成线程任务,丢入线程池执行;
- executor: 用来执行SendTask任务的TaskPoolExecutor线程池;
- interceptors :普通ChannelInterceptor集合;
- executorInterceptors:ExecutorChannelInterceptor线程池拦截器集合;
ChannelInterceptor与ExecutorChannelInterceptor到底有什么区别,并如何工作?
ExecutorSubscribableChannel:
public class ExecutorSubscribableChannel extends AbstractSubscribableChannel {
private String beanName;
private final Set<MessageHandler> handlers = new CopyOnWriteArraySet<>();
private final Executor executor;
private final List<ChannelInterceptor> interceptors = new ArrayList<>(5);
private final List<ExecutorChannelInterceptor> executorInterceptors = new ArrayList<>(4);
/**
* Invoke a MessageHandler with ExecutorChannelInterceptors.
*/
private class SendTask implements MessageHandlingRunnable {
}
}
三个ExecutorSubscribableChannel实例
在启用STOMP的时候——@EnableWebSocketMessageBroker,Spring框架会自动构造三个ExecutorSubscribableChannel实例:
- “clientInboundChannel” — 用于传递从WebSocket客户端接收到的消息。
- “clientOutboundChannel” — 用于向WebSocket客户端发送服务器消息。
- “brokerChannel” — 用于从服务器端的应用程序代码中向message broker或 stomp broker relay发送消息。
工作方式如下图所示:
启用简单的消息代理:config.enableSimpleBroker
启用STOMP代理中继:config.enableStompBrokerRelay
自定义配置MessageChannel
- 启用STOMP,并配置MessageChannel。从方法的名字上我们可以确定分别是对brokerChannel、clientInboundChannel、clientOutboundChannel进行自定义配置。
WebSocketMessageBrokerConfigurer实现类:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfigurer implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.configureBrokerChannel().taskExecutor();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
}
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
}
}
MessageChannel的默认配置
- 一直说通过@EnableWebSocketMessageBroker来启用STOMP,那么它是如何启用STOMP的呢?当然,它是通过导入相关配置来实现STOMP启用的。
EnableWebSocketMessageBroker:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebSocketMessageBrokerConfiguration.class)
public @interface EnableWebSocketMessageBroker {
}
- 配置clientInboundChannel。其中有
一个相关成员变量
和四个相关方法
:- 一个成员变量:
clientInboundChannelRegistration:用来登记“clientInboundChannel”的配置信息。 - 一个钩子方法:
configureClientInboundChannel(registration):留给它的子类,用来获取WebSocketMessageBrokerConfigurer
提供的自定义配置信息。 - 三个Bean方法:
- clientInboundChannel():通过
clientInboundChannelExecutor()
获得Executor实例,通过getClientInboundChannelRegistration()
获得通道的其他配置信息,用来构造一个ExecutorSubscribableChannel
实例做为“clientInboundChannel”; - clientInboundChannelExecutor():通过
getClientInboundChannelRegistration()
获得通道配置信息,再从通道配置信息中获得TaskExecutorRegistration
线程池配置信息,最后从TaskExecutorRegistration
获得ThreadPoolTaskExecutor 实例,作为“clientInboundChannel”的支撑; - getClientInboundChannelRegistration():如果成员变量
clientInboundChannelRegistration
为null
,那么将直接new
一个ChannelRegistration
实例,并赋值给成员变量clientInboundChannelRegistration
,同时调用configureClientInboundChannel(registration)
钩子方法,获取WebSocketMessageBrokerConfigurer
提供的自定义配置信息,否则就说明不是第一次调用该方法,直接返回成员变量;
- clientInboundChannel():通过
- 一个成员变量:
AbstractMessageBrokerConfiguration :
public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
@Nullable
private ChannelRegistration clientInboundChannelRegistration;
@Bean
public AbstractSubscribableChannel clientInboundChannel() {
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientInboundChannelExecutor());
ChannelRegistration reg = getClientInboundChannelRegistration();
if (reg.hasInterceptors()) {
channel.setInterceptors(reg.getInterceptors());
}
return channel;
}
@Bean
public ThreadPoolTaskExecutor clientInboundChannelExecutor() {
TaskExecutorRegistration reg = getClientInboundChannelRegistration().taskExecutor();
ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
executor.setThreadNamePrefix("clientInboundChannel-");
return executor;
}
protected final ChannelRegistration getClientInboundChannelRegistration() {
if (this.clientInboundChannelRegistration == null) {
ChannelRegistration registration = new ChannelRegistration();
configureClientInboundChannel(registration);
registration.interceptors(new ImmutableMessageChannelInterceptor());
this.clientInboundChannelRegistration = registration;
}
return this.clientInboundChannelRegistration;
}
/**
* A hook for subclasses to customize the message channel for inbound messages
* from WebSocket clients.
*/
protected void configureClientInboundChannel(ChannelRegistration registration) {
}
}
- 配置clientOutboundChannel。其中也有
一个相关成员变量
和四个相关方法
,在此不做详述,可以直接类比上面的“配置clientInboundChannel”。
AbstractMessageBrokerConfiguration :
public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
@Nullable
private ChannelRegistration clientOutboundChannelRegistration;
@Bean
public AbstractSubscribableChannel clientOutboundChannel() {
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientOutboundChannelExecutor());
ChannelRegistration reg = getClientOutboundChannelRegistration();
if (reg.hasInterceptors()) {
channel.setInterceptors(reg.getInterceptors());
}
return channel;
}
@Bean
public ThreadPoolTaskExecutor clientOutboundChannelExecutor() {
TaskExecutorRegistration reg = getClientOutboundChannelRegistration().taskExecutor();
ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
executor.setThreadNamePrefix("clientOutboundChannel-");
return executor;
}
protected final ChannelRegistration getClientOutboundChannelRegistration() {
if (this.clientOutboundChannelRegistration == null) {
ChannelRegistration registration = new ChannelRegistration();
configureClientOutboundChannel(registration);
registration.interceptors(new ImmutableMessageChannelInterceptor());
this.clientOutboundChannelRegistration = registration;
}
return this.clientOutboundChannelRegistration;
}
/**
* A hook for subclasses to customize the message channel for messages from
* the application or message broker to WebSocket clients.
*/
protected void configureClientOutboundChannel(ChannelRegistration registration) {
}
}
-
在看brokerChannel之前,有必要先要了解下
ChannelRegistration
——通道配置信息类。该类共持有两个实例:- TaskExecutorRegistration实例:
我们知道ExecutorSubscribableChannel
实际上是由ThreadPoolTaskExecutor
线程池作为支撑,而TaskExecutorRegistration
所持有的就是通道的ThreadPoolTaskExecutor
线程池配置信息; - ChannelInterceptor集合:
用来保存一系列的通道拦截器;
另外我们需要理解taskExecutor(taskExecutor)方法:
- 如果
taskExecutor()
方法不是第一次被调用,那么TaskExecutorRegistration
将不为null
,说明线程池已经被配置,将直接返回配置信息; - 如果
TaskExecutorRegistration
为null
,那么继续判断; - 参数
taskExecutor
如果不为null
,那么将把taskExecutor
绑定到TaskExecutorRegistration
——new TaskExecutorRegistration(taskExecutor); - 参数
taskExecutor
如果为null
,那么直接new TaskExecutorRegistration();
- TaskExecutorRegistration实例:
ChannelRegistration:
public class ChannelRegistration {
@Nullable
private TaskExecutorRegistration registration;
private final List<ChannelInterceptor> interceptors = new ArrayList<>();
/**
* Configure the thread pool backing this message channel.
*/
public TaskExecutorRegistration taskExecutor() {
return taskExecutor(null);
}
/**
* Configure the thread pool backing this message channel using a custom
* ThreadPoolTaskExecutor.
* @param taskExecutor the executor to use (or {@code null} for a default executor)
*/
public TaskExecutorRegistration taskExecutor(@Nullable ThreadPoolTaskExecutor taskExecutor) {
if (this.registration == null) {
this.registration = (taskExecutor != null ? new TaskExecutorRegistration(taskExecutor) :
new TaskExecutorRegistration());
}
return this.registration;
}
/**
* Configure the given interceptors for this message channel,
* adding them to the channel's current list of interceptors.
* @since 4.3.12
*/
public ChannelRegistration interceptors(ChannelInterceptor... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
return this;
}
/**
* Configure interceptors for the message channel.
* @deprecated as of 4.3.12, in favor of {@link #interceptors(ChannelInterceptor...)}
*/
@Deprecated
public ChannelRegistration setInterceptors(@Nullable ChannelInterceptor... interceptors) {
if (interceptors != null) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
return this;
}
protected boolean hasTaskExecutor() {
return (this.registration != null);
}
protected boolean hasInterceptors() {
return !this.interceptors.isEmpty();
}
protected List<ChannelInterceptor> getInterceptors() {
return this.interceptors;
}
}
- 接着再看
TaskExecutorRegistration
这个类,这个类持有一个ThreadPoolTaskExecutor
实例。这个类有两个构造方法:- TaskExecutorRegistration(taskExecutor):
将传入的taskExecutor
绑定到成员变量上。 - TaskExecutorRegistration():
无参的构造方法,在该构造方法中,会直接new
一个ThreadPoolTaskExecutor
实例,其coreSize
核心线程数为Runtime.getRuntime().availableProcessors() * 2 —— 两倍CPU
- TaskExecutorRegistration(taskExecutor):
TaskExecutorRegistration:
public class TaskExecutorRegistration {
private final ThreadPoolTaskExecutor taskExecutor;
/**
* Create a new {@code TaskExecutorRegistration} for a default
* {@link ThreadPoolTaskExecutor}.
*/
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) {
Assert.notNull(taskExecutor, "ThreadPoolTaskExecutor must not be null");
this.taskExecutor = taskExecutor;
}
protected ThreadPoolTaskExecutor getTaskExecutor() {
if (this.corePoolSize != null) {
this.taskExecutor.setCorePoolSize(this.corePoolSize);
}
if (this.maxPoolSize != null) {
this.taskExecutor.setMaxPoolSize(this.maxPoolSize);
}
if (this.keepAliveSeconds != null) {
this.taskExecutor.setKeepAliveSeconds(this.keepAliveSeconds);
}
if (this.queueCapacity != null) {
this.taskExecutor.setQueueCapacity(this.queueCapacity);
}
return this.taskExecutor;
}
}
- 配置brokerChannel。与clientInboundChannel、clientOutboundChannel不同的是,配置brokerChannel用
getBrokerRegistry()
代替了getClient**boundChannelRegistration()
、用configureMessageBroker(registry)
代替了configureClient**boundChannel(registration)
,主要是因为前两者只需要配置MessageChannel,而后者既需要配置MessageChannel同时需要配置MessageBroker,所以这里用MessageBrokerRegistry
代替了ChannelRegistration
,而MessageBrokerRegistry
持有了ChannelRegistration
实例,相当于多加了一层;
AbstractMessageBrokerConfiguration :
public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
@Nullable
private MessageBrokerRegistry brokerRegistry;
@Bean
public AbstractSubscribableChannel brokerChannel() {
ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration();
ExecutorSubscribableChannel channel = (reg.hasTaskExecutor() ?
new ExecutorSubscribableChannel(brokerChannelExecutor()) : new ExecutorSubscribableChannel());
reg.interceptors(new ImmutableMessageChannelInterceptor());
channel.setInterceptors(reg.getInterceptors());
return channel;
}
@Bean
public ThreadPoolTaskExecutor brokerChannelExecutor() {
ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration();
ThreadPoolTaskExecutor executor;
if (reg.hasTaskExecutor()) {
executor = reg.taskExecutor().getTaskExecutor();
}
else {
// Should never be used
executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(0);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(0);
}
executor.setThreadNamePrefix("brokerChannel-");
return executor;
}
/**
* An accessor for the {@link MessageBrokerRegistry} that ensures its one-time creation
* and initialization through {@link #configureMessageBroker(MessageBrokerRegistry)}.
*/
protected final MessageBrokerRegistry getBrokerRegistry() {
if (this.brokerRegistry == null) {
MessageBrokerRegistry registry = new MessageBrokerRegistry(clientInboundChannel(), clientOutboundChannel());
configureMessageBroker(registry);
this.brokerRegistry = registry;
}
return this.brokerRegistry;
}
/**
* A hook for subclasses to customize message broker configuration through the
* provided {@link MessageBrokerRegistry} instance.
*/
protected void configureMessageBroker(MessageBrokerRegistry registry) {
}
}
- 值得注意的是,
brokerChannel()
方法在构造ExecutorSubscribableChannel
实例时,绑定ThreadPoolTaskExecutor
的逻辑与前两者有所不同,结合上面的分析不难理解下列内容;- clientInboundChannel、clientOutboundChannel
这两个通道的线程池一定会被设置,所以Messages总会由新的线程异步处理 —— 首先考虑自定义配置线程池,如果没有,那么将配置默认线程池 —— 其coreSize核心线程数为Runtime.getRuntime().availableProcessors() * 2
—— 两倍CPUExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(client**boundChannelExecutor());
- brokerChannel
如果没有自定义配置线程池,那么brokerChannel
的线程池将被设为null
,Messages将会被使用当前线程同步处理,所以在生产环境中一定要配置brokerChannel
的线程池ExecutorSubscribableChannel channel = (reg.hasTaskExecutor() ? new ExecutorSubscribableChannel(brokerChannelExecutor()) : new ExecutorSubscribableChannel());
- clientInboundChannel、clientOutboundChannel
ExecutorSubscribableChannel :
public class ExecutorSubscribableChannel extends AbstractSubscribableChannel {
@Nullable
private final Executor executor;
/**
* Create a new {@link ExecutorSubscribableChannel} instance
* where messages will be sent in the callers thread.
*/
public ExecutorSubscribableChannel() {
this(null);
}
/**
* Create a new {@link ExecutorSubscribableChannel} instance
* where messages will be sent via the specified executor.
* @param executor the executor used to send the message,
* or {@code null} to execute in the callers thread.
*/
public ExecutorSubscribableChannel(@Nullable Executor executor) {
this.executor = executor;
}
}