谈到性能,没有银弹。许多因素都会影响它,包括消息的大小和数量,应用方法是否执行需要阻塞的工作,以及外部因素(如网络速度和其他问题)。本节的目的是提供一个可用配置选项的概述,以及关于如何推理扩展的一些想法。
在消息传递应用中,消息是通过通道传递的,用于由线程池支持的异步执行。配置这样的应用需要对通道和消息流有充分的了解。因此,建议回顾一下 信息流。
最明显的开始是配置支持 clientInboundChannel
和 clientOutboundChannel
的线程池。默认情况下,两者都被配置为可用处理器数量的两倍。
如果注释方法中的消息处理主要是由 CPU 控制的,那么 clientInboundChannel
的线程数量应该保持与处理器数量接近。如果它们所做的工作更多的是 IO-bound ,需要在数据库或其他外部系统上阻塞或等待,那么线程池的大小可能需要增加。
:::info
ThreadPoolExecutor 有三个重要的属性:核心线程池大小、最大线程池大小,以及用于存储没有可用线程的任务的队列容量。
一个常见的混淆点是,配置核心线程池大小(例如,10)和最大线程池大小(例如,20)的结果是一个有 10 到 20 个线程的线程池。事实上,如果容量保持在 Integer.MAX_VALUE 的默认值,线程池的增加永远不会超过核心池的大小,因为所有额外的任务都是排队的。
请参阅 ThreadPoolExecutor 的 javadoc,了解这些属性如何工作,并理解各种队列策略。
:::
在 clientOutboundChannel
方面,它是向 WebSocket 客户端发送消息的全部内容。如果客户在快速网络上,线程数应保持接近可用处理器的数量。如果他们速度慢或带宽低,他们需要更长的时间来消费消息,给线程池带来负担。因此,增加线程池的大小成为必要。
虽然 clientInboundChannel
的工作量是可以预测的—毕竟它是基于应用程序所做的事情—但如何配置 clientOutboundChannel
则比较困难,因为它是基于应用程序无法控制的因素。出于这个原因,有两个额外的属性与消息的发送有关:sendTimeLimit
和 sendBufferSizeLimit
。你可以使用这些方法来配置在向客户端发送消息时允许多长时间的发送以及可以缓冲多少数据。
一般的想法是,在任何时候,只有一个线程可以被用来向客户端发送。同时,所有额外的消息都会被缓冲,你可以使用这些属性来决定允许发送一个消息需要多长时间以及在此期间可以缓冲多少数据。有关其他重要的细节,请参见 javadoc 和 XML 模式的文档。
下面的例子显示了一个可能的配置:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.setSendTimeLimit(15 * 1000).setSendBufferSizeLimit(512 * 1024);
}
// ...
}
和等效的 xml 配置
<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>
关于扩展的一个重要观点涉及到使用多个应用程序实例。目前,您不能用简单的代理做到这一点。但是,当您使用全功能代理(如 RabbitMQ)时,每个应用程序实例都会连接到代理,并且从一个应用程序实例广播的消息可以通过代理广播给通过任何其他应用程序实例连接的 WebSocket 客户端。