REST vs WebSockets

1. 概要

このチュートリアルでは、クライアントとサーバー間の通信の基本について説明し、現在利用可能な2つの一般的なオプションを使用してこれを検証します。 新規参入者であるWebSocketが、RESTful HTTPのより一般的な選択にどのように反するかを見ていきます。

2. ネットワーク通信の基礎

さまざまなオプションとそのメリットとデメリットの詳細について詳しく説明する前に、ネットワーク通信の状況をすばやく更新しましょう。 これは、物事を視野に入れ、これをよりよく理解するのに役立ちます。
ネットワーク通信は、https://www.iso.org/ics/35.100/x/ [Open Systems Interconnection(OSI)model]の観点から最もよく理解できます。
  • OSIモデルは、通信システムを7つの抽象化層に分割します:*

    link:/uploads/OCI-Model-100x80.jpg%20100w []
    このモデルの最上部には、このチュートリアルで関心のあるアプリケーション層があります。 ただし、WebSocketとRESTful HTTPを比較しながら、上位4層のいくつかの側面について説明します。
    *アプリケーション層はエンドユーザーに最も近く、通信に参加しているアプリケーションとのインターフェースを担当します。* FTP、SMTP、SNMP、HTTP、WebSocketなど、この層で使用される一般的なプロトコルがいくつかあります。

3. WebSocketとRESTful HTTPの説明

通信は任意の数のシステム間で発生する可能性がありますが、特にクライアントとサーバー間の通信に関心があります。 具体的には、WebブラウザーとWebサーバー間の通信に焦点を当てます。 これは、WebSocketとRESTful HTTPを比較するために使用するフレームです。
しかし、さらに先に進む前に、それらが何であるかをすぐに理解しないでください!

3.1. WebSockets

正式な定義では、* https://tools.ietf.org/html/rfc6455 [WebSocket]は、永続的なTCP接続を介した双方向の全二重通信を特徴とする通信プロトコルです。*これで、このステートメントの各部分を詳しく説明します。
 
WebSocketは、2011年にIETFによってhttps://tools.ietf.org/html/rfc6455[RFC 6455]として通信プロトコルとして標準化されました。 今日のほとんどのWebブラウザーは、WebSocketプロトコルをサポートしています。

3.2. RESTful HTTP

インターネット上に広く存在しているため、私たち全員がhttps://tools.ietf.org/html/rfc2616[HTTP]を認識していますが、アプリケーション層の通信プロトコルでもあります。 * HTTPは要求/応答ベースのプロトコル*です。これについても、チュートリアルの後半で詳しく説明します。
  • REST(Representational State Transfer)は、Webサービスを作成するためにHTTP *に一連の制約を課すアーキテクチャスタイルです。

4. WebSocketサブプロトコル

WebSocketはクライアントとサーバー間の双方向通信のプロトコルを定義しますが、*交換されるメッセージには条件を付けません*。 これは、サブプロトコルネゴシエーションの一部として同意するために、通信の当事者に開放されたままになります。
自明でないアプリケーション用のサブプロトコルを開発することは便利ではありません。 幸いなことに、* https://stomp.github.io/ [STOMP]のような多くの一般的なサブプロトコルが使用可能です*。 STOMPはSimple Text Oriented Messaging Protocolの略で、WebSocket上で動作します。 Spring BootにはSTOMPのファーストクラスサポートがあり、これはチュートリアルで使用します。

5. Spring Bootのクイックセットアップ

実例を見ることほど良いものはありません。 そのため、WebSocketとRESTful HTTPの両方で単純なユースケースを作成して、それらをさらに調査し、比較します。 両方の単純なサーバーとクライアントコンポーネントを作成しましょう。
JavaScriptを使用して名前を送信する単純なクライアントを作成します。 そして、挨拶で応答するJavaを使用してサーバーを作成します。

5.1. WebSocket

Spring BootでWebSocketを使用するには、https://search.maven.org/artifact/org.springframework.boot/spring-boot-starter-websocket/2.1.4.RELEASE/jar [適切なスターター]が必要です。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
次に、STOMPエンドポイントを構成します。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws");
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/topic");
    }
}
名前を受け入れて挨拶で応答する単純なWebSocketサーバーをすばやく定義しましょう。
@Controller
public class WebSocketController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(Message message) throws Exception {
        return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
    }
}
最後に、このWebSocketサーバーと通信するクライアントを構築しましょう。 ブラウザからサーバーへの通信を重視しているため、JavaScriptでクライアントを作成しましょう。
var stompClient = null;
function connect() {
    stompClient = Stomp.client('ws://localhost:8080/ws');
    stompClient.connect({}, function (frame) {
        stompClient.subscribe('/topic/greetings', function (response) {
            showGreeting(JSON.parse(response.body).content);
        });
    });
}
function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
function showGreeting(message) {
    $("#greetings").append("<tr><td>" + message + "</td></tr>");
}
これで、WebSocketサーバーとクライアントの実例が完成しました。 コードリポジトリには、対話するシンプルなユーザーインターフェイスを提供するHTMLページがあります。
これはほんの表面的なことですが、https://www.baeldung.com/websockets-spring [Springを使用したWebSocketを使用して、複雑なチャットクライアントを構築できます]などがあります。

5.2. RESTful HTTP

ここで、RESTfulサービスの同様の設定を行います。 単純なWebサービスは、名前付きのGETリクエストを受け入れ、挨拶で応答します。
今回は代わりにhttps://search.maven.org/artifact/org.springframework.boot/spring-boot-starter-web/2.1.4.RELEASE/jar[Spring Boot's web starter]を使用しましょう。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
次に、Springで利用可能な強力な注釈サポートを活用するRESTエンドポイントを定義します。
@RestController
@RequestMapping(path = "/rest")
public class RestAPIController {
    @GetMapping(path="/{name}", produces = "application/json")
    public String getGreeting(@PathVariable("name") String name)
    {
        return "{\"greeting\" : \"Hello, " + name + "!\"}";
    }
}
最後に、JavaScriptでクライアントを作成しましょう。
var request = new XMLHttpRequest()
function sendName() {
    request.open('GET', 'http://localhost:8080/rest/'+$("#name").val(), true)
    request.onload = function () {
        var data = JSON.parse(this.response)
        showGreeting(data.greeting)
    }
    request.send()
}

function showGreeting(message) {
    $("#greetings").append("<tr><td>" + message + "</td></tr>");
}
それはほとんどそれです! 繰り返しになりますが、コードリポジトリには、ユーザーインターフェイスを操作するHTMLページがあります。
link:/rest-with-spring-series [プロダクショングレードのREST APIを定義する]は、そのシンプルさに深いものの、はるかに広範なタスクになる可能性があります。

6. WebSocketとRESTful HTTPの比較

WebSocketとRESTful HTTPの最小限の、しかし動作する例を作成したので、今度はそれらがどのように互いに対抗するかを理解する準備ができました。 これについては、次のサブセクションでいくつかの基準に対して検証します。
HTTPとWebSocketは両方ともアプリケーション層プロトコルであるため、直接比較することができますが、* RESTとWebSocket *を比較するのは自然ではないことに注意することが重要です。 先ほど見たように、RESTは通信にHTTPを活用するアーキテクチャスタイルです。
したがって、* WebSocketとの比較では、ほとんどの場合、HTTP *の機能またはその欠如が考慮されます。

6.1. URLスキーム

URLは、Webリソースの一意の場所とそれを取得するメカニズムを定義します*。 クライアント/サーバー通信では、多くの場合、関連するURLを介して静的または動的なリソースを取得しようとしています。
私たちは皆、HTTP URLスキームに精通しています:
http://localhost:8080/rest
WebSocket URLスキームもそれほど変わりません:
ws://localhost:8080/ws
最初は、コロンの前の文字だけが違いますが、内部で起こる多くのことを抽象化します。 さらに探検しましょう。

6.2. ハンドシェーク

_Handshake_ *通信パーティ間で通信プロトコルを自動的にネゴシエートする方法を指します*。 HTTPはステートレスプロトコルであり、要求/応答メカニズムで機能します。 すべてのHTTP要求で、ソケットを介してサーバーとのTCP接続が確立されます。
クライアントは、サーバーがリソースまたはエラーで応答するまで待機します。 クライアントからの次のリクエストは、前のリクエストが発生しなかったかのようにすべてを繰り返します。
link:/uploads/HTTP-Connection-100x43.jpg%20100w []
WebSocketの動作はHTTPとは大きく異なり、実際の通信の前にハンドシェイクで始まります。
WebSocketハンドシェイクの構成要素を見てみましょう。
link:/uploads/WebSocket-Connection-100x43.jpg%20100w []
WebSocketの場合、*クライアントはHTTPでプロトコルハンドシェイク要求を開始し、サーバーがHTTPからWebSocketへのアップグレードを受け入れると応答するまで待機します*。
もちろん、プロトコルハンドシェイクはHTTP経由で行われるため、前の図のシーケンスに従います。 ただし、接続が確立されると、そこからクライアントとサーバーはWebSocketに切り替わり、さらに通信を行います。

6.3. 接続

前のサブセクションで見たように、WebSocketとHTTPの大きな違いの1つは、WebSocketが永続的なTCP接続で動作し、HTTPがすべてのリクエストに対して新しいTCP接続を作成することです。
今では明らかに、すべてのリクエストに対して新しいTCP接続を作成することはそれほどパフォーマンスが高くなく、HTTPはこれを認識していません。 実際、HTTP / 1.1の一部として、このHTTPの欠点を軽減するために永続的な接続が導入されました。
それでも、* WebSocketは永続的なTCP接続で動作するようにゼロから設計されています*。

6.4. コミュニケーション

HTTPに対するWebSocketの利点は、クライアントが古き良きHTTPでは不可能だった方法でサーバーが通信できるという事実から生じる特定のシナリオです。
たとえば、HTTPでは、通常、クライアントがその要求を送信し、サーバーが要求されたデータで応答します。 サーバーがクライアントと単独で通信するための一般的な方法はありません。 もちろん、サーバー送信イベント(SSE)のようにこれを回避するためのパターンとソリューションが考案されましたが、これらは完全に自然ではありませんでした。
永続的なTCP通信で動作するWebSocketを使用すると、*サーバーとクライアントの両方が互いに独立してデータを送信することができます*、実際には多くの通信相手にデータを送信できます! *これは双方向通信と呼ばれます。*
  • WebSocket通信のもう1つの興味深い機能は、全二重であるということです。 この用語は難解に聞こえるかもしれませんが、サーバーとクライアントの両方が同時にデータを送信できることを意味します*。 これを、サーバーがデータで応答する前にリクエストを完全に受信するまで待たなければならないHTTPで起こることと比較してください。

    双方向および全二重通信の利点はすぐには明らかにならないかもしれません。 本当の力を解き放ついくつかのユースケースがあります。

6.5. セキュリティ

最後になりましたが、* HTTPとWebSocketの両方がTLSのセキュリティ上の利点を活用しています*。 HTTPはこれを使用するURLスキームの一部として_https_を提供しますが、WebSocketは同じ効果のためにURLスキームの一部として_wss_を提供します。
したがって、前のサブセクションのURLの保護されたバージョンは次のようになります。
https://localhost:443/rest
wss://localhost:443/ws
RESTfulサービスまたはWebSocket通信のいずれかを保護することは、かなりの深さの主題であり、ここではカバーできません。 今のところ、この点で両方が適切にサポートされているとだけ言っておきましょう。

7. それらはどこで使用すべきですか?

これで、HTTPを介したRESTfulサービスとWebSocketを介した単純な通信を十分に見て、それらについての意見を形成できました。 しかし、どこで何を使うべきでしょうか?
WebSocketはHTTPの欠点から抜け出しましたが、実際にはHTTPに置き換わるものではないことを覚えておくことが重要です。 だから、彼らは両方の場所とその用途を持っています。 どのように決定を下すことができるかをすぐに理解しましょう。
従業員の記録を取得するなど、サーバーとの時々の通信が必要なシナリオの大部分については、HTTP / S *経由でRESTサービスを使用するのが賢明です。 しかし、サーバーからのリアルタイムの更新を必要とする株価アプリケーションなどの新しいクライアント側アプリケーションの場合、WebSocketを活用すると非常に便利です。
一般化すると、* WebSocketは、プッシュベースのリアルタイム通信が要件をより適切に定義する場合により適しています*。 さらに、* WebSocketは、メッセージを複数のクライアントに同時にプッシュする必要があるシナリオに適しています*。 これらは、RESTfulサービスを介したクライアントとサーバーの通信が、禁止ではないとしても難しいと感じる場合です。
それでも、HTTPを介したWebSocketおよびRESTfulサービスの使用は、要件から引き出す必要があります。 特効薬がないように、すべての問題を解決するために1つだけを選ぶことは期待できません。 したがって、効率的なコミュニケーションモデルを設計するには、知識と知識を組み合わせて使用​​する必要があります。

8. 結論

このチュートリアルでは、アプリケーション層プロトコルHTTPおよびWebSocketに重点を置いて、ネットワーク通信の基本を確認しました。 Spring BootでWebSocketとHTTPを介したRESTful APIの簡単なデモを見ました。
最後に、HTTPプロトコルとWebSocketプロトコルの機能を比較し、それぞれを使用するタイミングについて簡単に説明しました。
いつものように、例のコードは利用可能ですhttps://github.com/eugenp/tutorials/tree/master/spring-security-mvc-socket[over on GitHub]。