パフォーマンス

パフォーマンスに関しては特効薬はありません。メッセージのサイズと量、アプリケーションメソッドがブロックを必要とする作業を実行するかどうか、および外部要因(ネットワーク速度やその他の課題など)など、多くの要因が影響します。このセクションの目的は、使用可能な構成オプションの概要と、スケーリングをどのように推論するかについての考えを提供することです。

メッセージングアプリケーションでは、メッセージはスレッドプールによってサポートされる非同期実行のチャネルを介して渡されます。このようなアプリケーションを構成するには、チャネルとメッセージの流れに関する十分な知識が必要です。メッセージの流れを確認することをお勧めします。

開始する明白な場所は、clientInboundChannel と clientOutboundChannel をサポートするスレッドプールを構成することです。デフォルトでは、両方とも使用可能なプロセッサーの数の 2 倍で構成されます。

アノテーション付きメソッドでのメッセージの処理が主に CPU バウンドである場合、clientInboundChannel のスレッドの数はプロセッサーの数に近いままである必要があります。それらが行う作業がより多くの IO バウンドであり、データベースまたは他の外部システムでのブロックまたは待機が必要な場合、おそらくスレッドプールサイズを増やす必要があります。

ThreadPoolExecutor には 3 つの重要なプロパティがあります。コアスレッドプールサイズ、最大スレッドプールサイズ、利用可能なスレッドがないタスクを格納するキューの容量です。

混乱の一般的なポイントは、コアプールサイズ(たとえば、10)と最大プールサイズ(たとえば、20)を構成すると、スレッドプールが 10 〜 20 スレッドになることです。実際、容量がデフォルト値の Integer.MAX_VALUE のままになっている場合、すべての追加タスクがキューに入れられるため、スレッドプールがコアプールサイズを超えて増加することはありません。

ThreadPoolExecutor の javadoc を参照して、これらのプロパティがどのように機能するかを学び、さまざまなキューイング戦略を理解しましょう。

clientOutboundChannel 側では、WebSocket クライアントにメッセージを送信することがすべてです。クライアントが高速ネットワーク上にある場合、スレッドの数は利用可能なプロセッサーの数に近いままである必要があります。速度が遅い場合や帯域幅が狭い場合、メッセージを消費してスレッドプールに負担をかけるのに時間がかかります。スレッドプールサイズを増やす必要があります。

clientInboundChannel のワークロードは予測可能ですが (結局のところ、アプリケーションの動作に基づいているため)、"clientOutboundChannel" の構成方法は、アプリケーションの制御外の要因に基づいているため、より困難です。このため、メッセージの送信に関連する 2 つの追加プロパティ (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>

前に示した WebSocket トランスポート構成を使用して、受信 STOMP メッセージの最大許容サイズを構成することもできます。理論上、WebSocket メッセージのサイズはほぼ無制限になります。実際には、WebSocket サーバーには制限が課されます (たとえば、Tomcat では 8K、Jetty では 64K)。このため、stomp-js/stompjs [GitHub] (英語) などの STOMP クライアントは、より大きな STOMP メッセージを 16K 境界で分割し、複数の WebSocket メッセージとして送信するため、サーバーでバッファリングして再組み立てする必要があります。

Spring の STOMP-over-WebSocket サポートがこれを行うため、WebSocket サーバー固有のメッセージサイズに関係なく、アプリケーションは STOMP メッセージの最大サイズを構成できます。WebSocket メッセージのサイズは、必要に応じて自動的に調整され、最小で 16K WebSocket メッセージを伝送できることを忘れないでください。

次の例は、可能な構成の 1 つを示しています。

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
		registration.setMessageSizeLimit(128 * 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 message-size="131072" />
		<!-- ... -->
	</websocket:message-broker>

</beans>

スケーリングに関する重要なポイントには、複数のアプリケーションインスタンスの使用が含まれます。現在、単純なブローカーではできません。ただし、フル機能のブローカー(RabbitMQ など)を使用する場合、各アプリケーションインスタンスはブローカーに接続し、1 つのアプリケーションインスタンスからブロードキャストされるメッセージは、ブローカーを介して他のアプリケーションインスタンスを介して接続される WebSocket クライアントにブロードキャストできます。