Spring Websocket+SockJS+STOMP 实现即时通信(三)—— MessageChannel

目录

    • 两种MessageChannel实现
      • TemporaryReplyChannel
      • ExecutorSubscribableChannel
    • 剖析ExecutorSubscribableChannel
    • 三个ExecutorSubscribableChannel实例
    • 自定义配置MessageChannel
    • MessageChannel的默认配置


两种MessageChannel实现

TemporaryReplyChannel

  • 用于接收单个回复消息的临时通道。在整个断点调试过程中没有追踪到,所以在这里不详细说明。

ExecutorSubscribableChannel

  • 正如字面上所表示的这样Executor(线程池)Subscribable(可订阅的)Channel(通道)——一个通过线程池将消息发送给每个订阅者的通道。这也是Spring-Messaging功能的核心,理解了这个实现类的构成,就很容易掌握《Spring Websocket+SockJS+STOMP 实现即时通信》

剖析ExecutorSubscribableChannel

Spring Websocket+SockJS+STOMP 实现即时通信(三)—— MessageChannel_第1张图片
ExecutorSubscribableChannel类,通过继承父类方法或直接声明,可以看作由 部分构成

  1. beanName:主要用作日志记录,用来区分ExecutorSubscribableChannel的不同实例;
  2. handlers:MessageHandler集合,作为MessageChannel的订阅者,用来处理Messages;
  3. SendTask :一个内部类,是MessageHandlingRunnable的子类,将一个Message与一个MessageHandler封装成线程任务,丢入线程池执行;
  4. executor: 用来执行SendTask任务的TaskPoolExecutor线程池;
  5. interceptors :普通ChannelInterceptor集合;
  6. 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
Spring Websocket+SockJS+STOMP 实现即时通信(三)—— MessageChannel_第2张图片
启用STOMP代理中继:config.enableStompBrokerRelay
Spring Websocket+SockJS+STOMP 实现即时通信(三)—— MessageChannel_第3张图片


自定义配置MessageChannel

  1. 启用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的默认配置

  1. 一直说通过@EnableWebSocketMessageBroker来启用STOMP,那么它是如何启用STOMP的呢?当然,它是通过导入相关配置来实现STOMP启用的。
EnableWebSocketMessageBroker:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebSocketMessageBrokerConfiguration.
     
      class
     )

     
      public
      @
     
      interface
      
     
      EnableWebSocketMessageBroker
      {

}

Spring Websocket+SockJS+STOMP 实现即时通信(三)—— MessageChannel_第4张图片

  1. 配置clientInboundChannel。其中有一个相关成员变量四个相关方法
    • 一个成员变量:
      clientInboundChannelRegistration:用来登记“clientInboundChannel”的配置信息。
    • 一个钩子方法:
      configureClientInboundChannel(registration):留给它的子类,用来获取WebSocketMessageBrokerConfigurer提供的自定义配置信息。
    • 三个Bean方法:
      • clientInboundChannel():通过clientInboundChannelExecutor()获得Executor实例,通过getClientInboundChannelRegistration()获得通道的其他配置信息,用来构造一个ExecutorSubscribableChannel 实例做为“clientInboundChannel”;
      • clientInboundChannelExecutor():通过getClientInboundChannelRegistration()获得通道配置信息,再从通道配置信息中获得TaskExecutorRegistration线程池配置信息,最后从TaskExecutorRegistration获得ThreadPoolTaskExecutor 实例,作为“clientInboundChannel”的支撑;
      • getClientInboundChannelRegistration():如果成员变量clientInboundChannelRegistrationnull,那么将直接new一个ChannelRegistration实例,并赋值给成员变量clientInboundChannelRegistration,同时调用configureClientInboundChannel(registration)钩子方法,获取WebSocketMessageBrokerConfigurer提供的自定义配置信息,否则就说明不是第一次调用该方法,直接返回成员变量;
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) {
	}
}
  1. 配置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) {
	}
}
  1. 在看brokerChannel之前,有必要先要了解下ChannelRegistration——通道配置信息类。该类共持有两个实例:

    • TaskExecutorRegistration实例:
      我们知道ExecutorSubscribableChannel实际上是由ThreadPoolTaskExecutor线程池作为支撑,而TaskExecutorRegistration所持有的就是通道的ThreadPoolTaskExecutor线程池配置信息;
    • ChannelInterceptor集合:
      用来保存一系列的通道拦截器;

    另外我们需要理解taskExecutor(taskExecutor)方法:

    1. 如果taskExecutor()方法不是第一次被调用,那么TaskExecutorRegistration将不为null,说明线程池已经被配置,将直接返回配置信息;
    2. 如果TaskExecutorRegistrationnull,那么继续判断;
    3. 参数taskExecutor如果不为null,那么将把taskExecutor绑定到TaskExecutorRegistration——new TaskExecutorRegistration(taskExecutor);
    4. 参数taskExecutor如果为null,那么直接new 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;
	}
}
  1. 接着再看TaskExecutorRegistration这个类,这个类持有一个ThreadPoolTaskExecutor实例。这个类有两个构造方法:
    • TaskExecutorRegistration(taskExecutor):
      将传入的taskExecutor绑定到成员变量上。
    • TaskExecutorRegistration():
      无参的构造方法,在该构造方法中,会直接new一个ThreadPoolTaskExecutor实例,其coreSize核心线程数为Runtime.getRuntime().availableProcessors() * 2 —— 两倍CPU
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;
	}
}
  1. 配置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) {
	}
}
  1. 值得注意的是,brokerChannel()方法在构造ExecutorSubscribableChannel实例时,绑定ThreadPoolTaskExecutor的逻辑与前两者有所不同,结合上面的分析不难理解下列内容;
    • clientInboundChannel、clientOutboundChannel
      这两个通道的线程池一定会被设置,所以Messages总会由新的线程异步处理 —— 首先考虑自定义配置线程池,如果没有,那么将配置默认线程池 —— 其coreSize核心线程数为Runtime.getRuntime().availableProcessors() * 2 —— 两倍CPU
      ExecutorSubscribableChannel channel = new 
               
                ExecutorSubscribableChannel
               (client**
               
                boundChannelExecutor
               ());
      
    • brokerChannel
      如果没有自定义配置线程池,那么brokerChannel的线程池将被设为null,Messages将会被使用当前线程同步处理,所以在生产环境中一定要配置brokerChannel的线程池
       ExecutorSubscribableChannel channel = (reg.
               
                hasTaskExecutor
               () ?
         		new 
               
                ExecutorSubscribableChannel
               (
               
                brokerChannelExecutor
               ()) : new 
               
                ExecutorSubscribableChannel
               ());
      
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;
	}
}

你可能感兴趣的:(Spring,Websocket)