WebSocketプロトコルは、アプリケーションにリアルタイムメッセージを処理させる方法の1つです。最も一般的な代替手段は、長いポーリングとサーバー送信イベントです。これらのソリューションにはそれぞれ長所と短所があります。この記事では、Spring BootFrameworkを使用してWebSocketを実装する方法を紹介します。サーバー側とクライアント側の両方のセットアップについて説明し、STOMP overWebSocketプロトコルを使用して相互に通信します。
サーバー側は純粋にJavaでコーディングされます。ただし、クライアントの場合、通常はWebSocketクライアントがフロントエンドアプリケーションに埋め込まれているため、JavaとJavaScript(SockJS)の両方で記述されたスニペットを示します。コード例では、pub-subモデルを使用して複数のユーザーにメッセージをブロードキャストする方法と、1人のユーザーにのみメッセージを送信する方法を示します。記事のさらなる部分では、WebSocketの保護と、環境がWebSocketプロトコルをサポートしていない場合でもWebSocketベースのソリューションが動作し続けることを保証する方法について簡単に説明します。
WebSocketの保護のトピックは、別の記事としては十分に複雑なトピックであるため、ここでは簡単に触れるだけであることに注意してください。これと私が触れている他のいくつかの要因のために WebSocketは本番環境にありますか? 最後のセクション、 このセットアップを本番環境で使用する前に、変更を加えることをお勧めします 、セキュリティ対策を講じた本番環境に対応したセットアップについては、最後までお読みください。
WebSocketプロトコルを使用すると、アプリケーション間の双方向通信を実装できます。 HTTPは最初のハンドシェイクにのみ使用されることを知っておくことが重要です。それが起こった後、HTTP接続はWebSocketによって使用される新しく開かれたTCP / IP接続にアップグレードされます。
WebSocketプロトコルは、かなり低レベルのプロトコルです。バイトのストリームをフレームに変換する方法を定義します。フレームには、テキストまたはバイナリメッセージを含めることができます。メッセージ自体は、メッセージのルーティングまたは処理方法に関する追加情報を提供しないため、追加のコードを記述せずに、より複雑なアプリケーションを実装することは困難です。幸い、WebSocket仕様では、より高いアプリケーションレベルで動作するサブプロトコルを使用できます。 Spring Frameworkでサポートされているものの1つは、STOMPです。
STOMPは、Ruby、Python、Perlなどのスクリプト言語がエンタープライズメッセージブローカーに接続するために最初に作成された、単純なテキストベースのメッセージングプロトコルです。 STOMPのおかげで、異なる言語で開発されたクライアントとブローカーは、相互にメッセージを送受信できます。 WebSocketプロトコルは、TCP forWebと呼ばれることもあります。同様に、STOMPはHTTP forWebと呼ばれます。これは、WebSocketフレームにマップされる少数のフレームタイプを定義します。たとえば、CONNECT
、SUBSCRIBE
、UNSUBSCRIBE
、ACK
、SEND
などです。これらのコマンドは、通信を管理するのに非常に便利ですが、メッセージの確認など、より高度な機能を備えたソリューションを実装することもできます。
WebSocketサーバー側を構築するために、JavaでのスタンドアロンおよびWebアプリケーションの開発を大幅に高速化するSpringBootフレームワークを利用します。 SpringBootにはspring-WebSocket
が含まれていますJava WebSocket API標準と互換性のあるモジュール( JSR-356 )。
Spring Bootを使用してサーバー側でWebSocketを実装することは、それほど複雑なタスクではなく、いくつかの手順のみが含まれています。これらの手順を1つずつ説明します。
ステップ1。 まず、WebSocketライブラリの依存関係を追加する必要があります。
org.springframework.boot spring-boot-starter-websocket
送信されるメッセージにJSON形式を使用する場合は、GSONまたはJacksonの依存関係も含めることをお勧めします。おそらく、SpringSecurityなどのセキュリティフレームワークがさらに必要になる可能性があります。
ステップ2。 次に、WebSocketおよびSTOMPメッセージングを有効にするようにSpringを構成できます。
Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint('/mywebsockets') .setAllowedOrigins('mydomain.com').withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry config){ config.enableSimpleBroker('/topic/', '/queue/'); config.setApplicationDestinationPrefixes('/app'); } }
メソッドconfigureMessageBroker
2つのことをします:
topic
およびqueue
。これらは、pub-subモデルを介してサブスクライブされたすべてのクライアントに送信されるメッセージの宛先の前にtopic
を付ける必要があるという規則に従います。一方、プライベートメッセージの宛先には、通常、プレフィックスとしてqueue
が付いています。app
これは、@MessageMapping
アノテーションが付けられたメソッドによって処理される宛先をフィルタリングするために使用されますこれをコントローラーに実装します。コントローラは、メッセージを処理した後、メッセージをブローカーに送信します。
上記のスニペットに戻ると(おそらくメソッドwithSockJS()
の呼び出しに気付いたでしょう)、SockJSフォールバックオプションが有効になっています。簡潔にするために、WebSocketプロトコルがインターネットブラウザーでサポートされていない場合でも、WebSocketを機能させることができます。このトピックについては、もう少し詳しく説明します。
明確にする必要があるもう1つのことがあります。それは、なぜsetAllowedOrigins()
と呼ぶのかということです。エンドポイントのメソッド。 WebSocketとSockJSのデフォルトの動作は、同一生成元のリクエストのみを受け入れることであるため、これが必要になることがよくあります。したがって、クライアントとサーバー側が異なるドメインを使用する場合、それらの間の通信を許可するためにこのメソッドを呼び出す必要があります。
知覚組織のゲシュタルト法
ステップ3 。ユーザーの要求を処理するコントローラーを実装します。特定のトピックにサブスクライブしているすべてのユーザーに受信メッセージをブロードキャストします。
これは、宛先/topic/news
にメッセージを送信するサンプルメソッドです。
@MessageMapping('/news') @SendTo('/topic/news') public void broadcastNews(@Payload String message) { return message; }
注釈@SendTo
の代わりに、SimpMessagingTemplate
を使用することもできますこれは、コントローラー内で自動配線できます。
@MessageMapping('/news') public void broadcastNews(@Payload String message) { this.simpMessagingTemplate.convertAndSend('/topic/news', message) }
後の手順で、エンドポイントを保護するために、ResourceServerConfigurerAdapter
などのクラスを追加することをお勧めします。またはWebSecurityConfigurerAdapter
SpringSecurityフレームワークから。また、送信されたJSONをオブジェクトにマッピングできるように、メッセージモデルを実装すると便利なことがよくあります。
クライアントの実装はさらに簡単な作業です。
手順1.SpringSTOMPクライアントを自動配線します。
@Autowired private WebSocketStompClient stompClient;
手順2.接続を開きます。
StompSessionHandler sessionHandler = new CustmStompSessionHandler(); StompSession stompSession = stompClient.connect(loggerServerQueueUrl, sessionHandler).get();
これが完了すると、宛先にメッセージを送信することが可能になります。メッセージは、トピックにサブスクライブしているすべてのユーザーに送信されます。
stompSession.send('topic/greetings', 'Hello new user');
メッセージを購読することも可能です。
session.subscribe('topic/greetings', this); @Override public void handleFrame(StompHeaders headers, Object payload) { Message msg = (Message) payload; logger.info('Received : ' + msg.getText()+ ' from : ' + msg.getFrom()); }
専用ユーザーにのみメッセージを送信する必要がある場合があります(たとえば、チャットを実装する場合)。次に、クライアントとサーバー側は、このプライベート会話専用の個別の宛先を使用する必要があります。宛先の名前は、一般的な宛先名に一意の識別子を追加することで作成できます(例:/queue/chat-user123
)。この目的のために、HTTPセッションまたはSTOMPセッション識別子を利用できます。
デザインの原則と要素
Springを使用すると、プライベートメッセージの送信がはるかに簡単になります。コントローラのメソッドに@SendToUser
で注釈を付けるだけで済みます。次に、この宛先は、セッション識別子に依存するUserDestinationMessageHandler
によって処理されます。クライアント側では、クライアントがプレフィックスが/user
の宛先にサブスクライブすると、この宛先はこのユーザーに固有の宛先に変換されます。サーバー側では、ユーザーの宛先はユーザーのPrincipal
に基づいて解決されます。
@SendToUser
を使用したサーバー側コードのサンプル注釈:
@MessageMapping('/greetings') @SendToUser('/queue/greetings') public String reply(@Payload String message, Principal user) { return 'Hello ' + message; }
または、SimpMessagingTemplate
を使用できます :
String username = ... this.simpMessagingTemplate.convertAndSendToUser(); username, '/queue/greetings', 'Hello ' + username);
次に、上記の例のJavaコードで送信できるプライベートメッセージを受信できるJavaScript(SockJS)クライアントを実装する方法を見てみましょう。 WebSocketはHTML5仕様の一部であり、最新のブラウザーのほとんどでサポートされていることを知っておく価値があります(Internet Explorerはバージョン10以降でサポートされています)。
function connect() { var socket = new SockJS('/greetings'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { stompClient.subscribe('/user/queue/greetings', function (greeting) { showGreeting(JSON.parse(greeting.body).name); }); }); } function sendName() { stompClient.send('/app/greetings', {}, $('#name').val()); }
おそらくお気づきかもしれませんが、プライベートメッセージを受信するには、クライアントは一般的な宛先にサブスクライブする必要があります/queue/greetings
接頭辞/user
。一意の識別子を気にする必要はありません。ただし、クライアントは事前にアプリケーションにログインする必要があるため、Principal
サーバー側のオブジェクトが初期化されます。
多くのWebアプリケーションはCookieベースの認証を使用しています。たとえば、Spring Securityを使用して、特定のページまたはコントローラーへのアクセスをログに記録されたユーザーのみに制限できます。その後、ユーザーのセキュリティコンテキストは、そのユーザー用に作成されたWebSocketまたはSockJSセッションに後で関連付けられるCookieベースのHTTPセッションを通じて維持されます。 WebSocketエンドポイントは、SpringのWebSecurityConfigurerAdapter
など、他のリクエストと同様に保護できます。
現在、Webアプリケーションは、バックエンドとしてREST APIを使用し、ユーザーの認証と承認のためにOAuth / JWTトークンを使用することがよくあります。 WebSocketプロトコルは、サーバーがHTTPハンドシェイク中にクライアントを認証する方法を記述していません。実際には、この目的のために標準のHTTPヘッダー(承認など)が使用されます。残念ながら、すべてのSTOMPクライアントでサポートされているわけではありません。 Spring JavaのSTOMPクライアントでは、ハンドシェイクのヘッダーを設定できます。
WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders(); handshakeHeaders.add(principalRequestHeader, principalRequestValue);
ただし、SockJS JavaScriptクライアントは、SockJSリクエストを使用した認証ヘッダーの送信をサポートしていません。ただし、トークンの受け渡しに使用できるクエリパラメータを送信することはできます。このアプローチでは、クエリパラメータからトークンを読み取り、検証するカスタムコードをサーバー側で作成する必要があります。重大なセキュリティ違反が発生する可能性があるため、トークンがリクエストと一緒にログに記録されないようにする(またはログが十分に保護される)ようにすることも重要です。
WebSocketとの統合は必ずしもスムーズに進むとは限りません。一部のブラウザー(IE 9など)はWebSocketをサポートしていません。さらに、制限プロキシを使用すると、HTTPアップグレードを実行できなくなったり、開いている接続が長すぎたりする可能性があります。そのような場合、SockJSが助けになります。
SockJSトランスポートは、WebSocket、HTTPストリーミング、およびHTTPロングポーリングの3つの一般的なカテゴリに分類されます。通信は、SockJSがGET /info
を送信することから始まります。サーバーから基本情報を取得します。応答に基づいて、SockJSは使用するトランスポートを決定します。最初の選択肢はWebSocketです。それらがサポートされていない場合は、可能であれば、ストリーミングが使用されます。このオプションも不可能な場合は、転送方法としてポーリングが選択されます。
この設定は機能しますが、「最良」ではありません。 Spring Bootを使用すると、STOMPプロトコル(ActiveMQ、RabbitMQなど)で本格的なメッセージングシステムを使用できます。外部ブローカーは、使用した単純なブローカーよりも多くのSTOMP操作(確認応答、受信など)をサポートする場合があります。 STOMP Over WebSocket WebSocketとSTOMPプロトコルに関する興味深い情報を提供します。 STOMPプロトコルを処理するメッセージングシステムがリストされており、本番環境で使用するためのより優れたソリューションになる可能性があります。特に、要求の数が多いために、メッセージブローカーをクラスター化する必要がある場合。 (Springのシンプルメッセージブローカーはクラスタリングには適していません。)次に、WebSocketConfig
でシンプルブローカーを有効にする代わりに、外部メッセージブローカーとの間でメッセージを転送するStompブローカーリレーを有効にする必要があります。要約すると、外部メッセージブローカーは、よりスケーラブルで堅牢なソリューションの構築に役立つ場合があります。
続行する準備ができている場合 Java開発者 Spring Bootを探索する旅、読んでみてください OAuth2とJWTREST保護にSpringBootを使用する 次。
STOMPは、シンプルな(またはストリーミング)テキスト指向のメッセージングプロトコルです。 CONNECT、SEND、SUBSCRIBEなどの一連のコマンドを使用して会話を管理します。任意の言語で記述されたSTOMPクライアントは、プロトコルをサポートする任意のメッセージブローカーと通信できます。
WebSocketは通常、Webアプリケーションをよりインタラクティブにするために使用されます。これらは、ソーシャルフィード、オンラインチャット、ニュースの更新、またはロケーションベースのアプリを実装するときに役立ちます。
WebSocketは、単一のTCP接続を介して双方向通信チャネルを提供します。クライアントは、WebSocketハンドシェイクと呼ばれるプロセスを通じて永続的な接続を確立します。この接続により、メッセージをリアルタイムで交換できます。
Spring Bootは、スタンドアロンアプリケーションまたはマイクロサービスの実装を容易にするJavaベースのフレームワークです。さまざまな製品やフレームワークとの統合が大幅に簡素化されるため、一般的に使用されます。また、組み込みのWebサーバーが含まれているため、WARファイルをデプロイする必要はありません。