对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

性能

在性能方面,并不存在一劳永逸的解决方案。许多因素都会影响性能,包括消息的大小和数量、应用程序方法是否执行需要阻塞的操作,以及外部因素(例如网络速度及其他问题)。本节旨在概述可用的配置选项,并就如何思考系统扩展性提供一些思路。spring-doc.cadn.net.cn

在消息传递应用程序中,消息通过通道进行传递,以实现由线程池支持的异步执行。配置此类应用程序需要对通道及消息的流动有充分的了解。因此,建议查阅消息的流动spring-doc.cadn.net.cn

显然,首先要配置的是支撑 clientInboundChannelclientOutboundChannel 的线程池。默认情况下,这两个线程池的大小均被设置为可用处理器数量的两倍。spring-doc.cadn.net.cn

如果带注解方法中的消息处理主要是 CPU 密集型的,那么 clientInboundChannel 的线程数量应保持接近处理器核心数。如果这些任务更多是 I/O 密集型的,并且需要阻塞或等待数据库或其他外部系统,则可能需要增加线程池的大小。spring-doc.cadn.net.cn

ThreadPoolExecutor 有三个重要属性:核心线程池大小、最大线程池大小,以及用于存储没有可用线程执行的任务的队列容量。spring-doc.cadn.net.cn

一个常见的误解是,配置核心线程池大小(例如10)和最大线程池大小(例如20)会得到一个包含10到20个线程的线程池。 实际上,如果队列容量保持默认值 Integer.MAX_VALUE 不变, 线程池永远不会超过核心线程池大小,因为所有额外的任务都会被放入队列中。spring-doc.cadn.net.cn

请参阅 ThreadPoolExecutor 的 Javadoc,以了解这些属性的工作原理,并理解各种队列策略。spring-doc.cadn.net.cn

clientOutboundChannel 这一侧,所有操作都是向 WebSocket 客户端发送消息。如果客户端处于高速网络中,线程数量应保持接近可用处理器的数量。如果客户端速度较慢或带宽较低,它们消耗消息的时间会更长,从而给线程池带来负担。因此,增加线程池的大小就变得必要了。spring-doc.cadn.net.cn

虽然 clientInboundChannel 的工作负载是可以预测的——毕竟它取决于应用程序本身的行为——但配置“clientOutboundChannel”则更加困难,因为其受应用程序无法控制的因素影响。因此,有两个额外的属性与消息发送相关:sendTimeLimitsendBufferSizeLimit。您可以使用这些属性来配置向客户端发送消息时允许的最大耗时以及可缓冲的数据量。spring-doc.cadn.net.cn

其基本思想是,在任意给定时刻,只能使用一个线程向客户端发送数据。与此同时,所有额外的消息都会被缓冲起来,你可以使用这些属性来决定发送一条消息允许花费多长时间,以及在此期间可以缓冲多少数据。有关重要的附加细节,请参阅 JavaDoc 和 XML Schema 文档。spring-doc.cadn.net.cn

以下示例展示了一种可能的配置:spring-doc.cadn.net.cn

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
		registration.setSendTimeLimit(15 * 1000).setSendBufferSizeLimit(512 * 1024);
	}

	// ...

}

以下示例展示了与前述示例等效的 XML 配置:spring-doc.cadn.net.cn

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/websocket
		https://www.springframework.org/schema/websocket/spring-websocket.xsd">

	<websocket:message-broker>
		<websocket:transport send-timeout="15000" send-buffer-size="524288" />
		<!-- ... -->
	</websocket:message-broker>

</beans>

您还可以使用前面展示的 WebSocket 传输配置来设置传入 STOMP 消息的最大允许大小。理论上,WebSocket 消息的大小几乎没有限制。但实际上,WebSocket 服务器会施加限制——例如,Tomcat 为 8K,Jetty 为 64K。因此,像 stomp-js/stompjs 等 STOMP 客户端会将较大的 STOMP 消息按 16K 边界拆分,并作为多个 WebSocket 消息发送,这需要服务器进行缓冲和重新组装。spring-doc.cadn.net.cn

Spring 的 STOMP-over-WebSocket 支持实现了这一点,因此应用程序可以配置 STOMP 消息的最大大小,而不受 WebSocket 服务器特定消息大小的限制。请注意,如有必要,WebSocket 消息大小会自动调整,以确保至少能够承载 16KB 的 WebSocket 消息。spring-doc.cadn.net.cn

以下示例展示了一种可能的配置:spring-doc.cadn.net.cn

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
		registration.setMessageSizeLimit(128 * 1024);
	}

	// ...

}

以下示例展示了与前述示例等效的 XML 配置:spring-doc.cadn.net.cn

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/websocket
		https://www.springframework.org/schema/websocket/spring-websocket.xsd">

	<websocket:message-broker>
		<websocket:transport message-size="131072" />
		<!-- ... -->
	</websocket:message-broker>

</beans>

关于扩展性,有一个重要要点涉及使用多个应用程序实例。 目前,使用简单代理(simple broker)无法实现这一点。 然而,当你使用功能齐全的代理(例如 RabbitMQ)时,每个应用程序实例都会连接到该代理, 并且从一个应用程序实例广播的消息可以通过代理转发给通过其他任何应用程序实例连接的 WebSocket 客户端。spring-doc.cadn.net.cn