RESTとWebSocket
1. 概要
このチュートリアルでは、クライアント/サーバー通信の基本を説明し、現在利用可能な2つの一般的なオプションを通じてこれを探ります。 新規参入者であるWebSocketが、より一般的なRESTfulHTTPの選択肢にどのように対抗するかを見ていきます。
2. ネットワーク通信の基礎
さまざまなオプションとその長所と短所の詳細を詳しく説明する前に、ネットワーク通信の状況をすばやく更新しましょう。 これは、物事を見通しに入れ、これをよりよく理解するのに役立ちます。
ネットワーク通信は、オープンシステム相互接続(OSI)モデルの観点から最もよく理解できます。
OSIモデルは、通信システムを7つの抽象化レイヤーに分割します。
このモデルの最上位には、このチュートリアルで関心のあるアプリケーション層があります。 ただし、WebSocketとRESTful HTTPを比較しながら、上位4つのレイヤーのいくつかの側面について説明します。
アプリケーション層はエンドユーザーに最も近く、通信に参加しているアプリケーションとのインターフェースを担当します。 FTP、SMTP、 SNMP、HTTP、およびWebSocket。
3. WebSocketとRESTfulHTTPの説明
通信は任意の数のシステム間で発生する可能性がありますが、特にクライアント/サーバー通信に関心があります。 具体的には、WebブラウザとWebサーバー間の通信に焦点を当てます。 これは、WebSocketとRESTfulHTTPを比較するために使用するフレームです。
しかし、先に進む前に、それらが何であるかをすぐに理解してみませんか?
3.1. WebSocket
正式な定義として、 WebSocketは、永続的なTCP接続を介した双方向の全二重通信を特徴とする通信プロトコルです。これで、このステートメントの各部分を詳しく理解していきます。 。
WebSocketは、2011年にIETFによって RFC6455として通信プロトコルとして標準化されました。 今日のほとんどの最新のWebブラウザーは、WebSocketプロトコルをサポートしています。
3.2. RESTful HTTP
HTTP はインターネット上に遍在しているため、私たち全員が知っていますが、これはアプリケーション層の通信プロトコルでもあります。 HTTPは要求/応答ベースのプロトコルです。これについても、チュートリアルの後半で詳しく説明します。
REST(Representational State Transfer)は、HTTPに一連の制約を課してWebサービスを作成するアーキテクチャスタイルです。
4. WebSocketサブプロトコル
WebSocketはクライアントとサーバー間の双方向通信のプロトコルを定義しますが、交換されるメッセージに条件を設定しません。 これは、通信の当事者がサブプロトコル交渉の一部として合意するために開かれたままです。
重要なアプリケーション用のサブプロトコルを開発するのは便利ではありません。 幸い、STOMPのような人気のあるサブプロトコルが多数あります。 STOMPはSimpleTextOriented Messaging Protocolの略で、WebSocket上で機能します。 Spring Bootは、チュートリアルで使用するSTOMPのファーストクラスサポートを備えています。
5. SpringBootでのクイックセットアップ
実例を見ることほど良いことはありません。 そこで、WebSocketとRESTful HTTPの両方で単純なユースケースを構築して、それらをさらに調査し、比較します。 両方の単純なサーバーとクライアントコンポーネントを作成しましょう。
名前を送信するJavaScriptを使用して単純なクライアントを作成します。 そして、あいさつで応答するJavaを使用してサーバーを作成します。
5.1. WebSocket
Spring BootでWebSocketを使用するには、適切なスターターが必要です。
<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ページがあります。
これは表面的な問題ですが、 WebSocket with Springを使用して、複雑なチャットクライアントなどを構築できます。
5.2. RESTful HTTP
ここで、RESTfulサービスの同様の設定を行います。 私たちのシンプルなWebサービスは、名前付きのGETリクエストを受け入れ、挨拶で応答します。
今回は代わりにSpring BootのWebスターターを使用しましょう。
<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ページがあります。
そのシンプルさには深遠ですが、プロダクショングレードのREST API を定義することは、はるかに広範なタスクになる可能性があります。
6. WebSocketとRESTfulHTTPの比較
WebSocketとRESTfulHTTPの最小限の、しかし機能する例を作成したので、これらが互いにどのように機能するかを理解する準備が整いました。 次のサブセクションでは、これをいくつかの基準と照らし合わせて検討します。
HTTPとWebSocketはどちらもアプリケーション層プロトコルであるため直接比較できますが、RESTをWebSocketと比較するのは自然ではないことに注意してください。 前に見たように、RESTは通信にHTTPを利用するアーキテクチャスタイルです。
したがって、 WebSocketとの比較は、主にHTTPの機能またはその欠如に関するものです。
6.1. URLスキーム
URL は、Webリソースの一意の場所とそれを取得するメカニズムを定義します。 クライアント/サーバー通信では、多くの場合、関連付けられたURLを介して静的または動的なリソースを取得しようとしています。
私たちは皆、HTTPURLスキームに精通しています。
http://localhost:8080/rest
WebSocketのURLスキームもそれほど違いはありません。
ws://localhost:8080/ws
最初は、コロンの前の文字だけが異なるように見えますが、それは内部で発生する多くのことを抽象化します。 さらに詳しく見ていきましょう。
6.2. ハンドシェーク
ハンドシェイクは、通信する当事者間で通信プロトコルを自動的にネゴシエートする方法を指します。 HTTPはステートレスプロトコルであり、要求/応答メカニズムで機能します。 HTTPリクエストごとに、ソケットを介してサーバーとのTCP接続が確立されます。
次に、クライアントはサーバーがリソースまたはエラーで応答するまで待機します。 クライアントからの次の要求は、前の要求が発生しなかったかのようにすべてを繰り返します。
WebSocketの動作はHTTPとは大きく異なり、実際の通信の前にハンドシェイクから始まります。
WebSocketハンドシェイクの構成要素を見てみましょう。
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)のようにこれを回避するためのパターンとソリューションが考案されていますが、これらは完全に自然なものではありませんでした。
WebSocketを使用すると、永続的なTCP通信を処理します。サーバーとクライアントの両方が互いに独立してデータを送信することができます。実際、多くの通信当事者に送信できます。 これは双方向通信と呼ばれます。
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通信のいずれかを保護することは非常に重要な問題であり、ここでは取り上げることができません。 今のところ、この点で両方が適切にサポートされているとだけ言っておきましょう。
6.6. パフォーマンス
WebSocketは、専用のTCP接続を介して通信が行われるステートフルプロトコルであることを理解する必要があります。 一方、HTTPは本質的にステートレスプロトコルです。 これは、これらが負荷でどのように機能するかに影響を与えますが、実際にはユースケースによって異なります。
WebSocketを介した通信は再利用可能なTCP接続を介して行われるため、メッセージあたりのオーバーヘッドはHTTPと比較して低くなります。 したがって、サーバーあたりのスループットが高くなる可能性があります。 しかし、単一のサーバーが拡張できる制限があり、それがWebSocketに問題があるところです。 WebSocketを使用してアプリケーションを水平方向にスケーリングすることは簡単ではありません。
これがHTTPが優れているところです。 HTTPを使用すると、新しいリクエストはそれぞれ、任意のサーバーに到達する可能性があります。 これは、全体的なスループットを向上させるために、サーバーを簡単に追加できることを意味します。 これは、HTTPで実行されているアプリケーションに影響を与えない可能性があります。
明らかに、アプリケーション自体が状態とセッションのスティッキーを必要とする場合があります。
7. どこで使うべきですか?
これで、HTTPを介したRESTfulサービスと、WebSocketを介した単純な通信を十分に確認して、それらに関する意見を形成することができました。 しかし、どこで何を使うべきでしょうか?
WebSocketはHTTPの欠点から生まれましたが、実際にはHTTPに置き換わるものではないことを覚えておくことが重要です。 したがって、どちらにも場所と用途があります。 どうすれば決定できるかをすぐに理解しましょう。
従業員の記録を取得するなど、サーバーとの通信が時折必要になるシナリオの大部分では、HTTP /Sを介してRESTサービスを使用するのが賢明です。 ただし、サーバーからのリアルタイム更新を必要とする株価アプリケーションなどの新しいクライアント側アプリケーションの場合、WebSocketを利用すると非常に便利です。
一般的に、 WebSocketは、プッシュベースのリアルタイム通信が要件をより適切に定義する場合に適しています。 さらに、 WebSocketは、メッセージを複数のクライアントに同時にプッシュする必要があるシナリオでうまく機能します。 これらは、RESTfulサービスを介したクライアントとサーバーの通信が、禁止ではないにしても困難であると感じる場合です。
それでも、HTTPを介したWebSocketおよびRESTfulサービスの使用は、要件から引き出す必要があります。 特効薬がないように、すべての問題を解決するために特効薬を選ぶことだけを期待することはできません。 したがって、効率的なコミュニケーションモデルを設計するには、知識と知識を組み合わせて使用する必要があります。
8. 結論
このチュートリアルでは、アプリケーション層プロトコルのHTTPとWebSocketに重点を置いて、ネットワーク通信の基本を確認しました。 SpringBootでのWebSocketとRESTfulAPIoverHTTPの簡単なデモンストレーションを見ました。
最後に、HTTPプロトコルとWebSocketプロトコルの機能を比較し、それぞれをいつ使用するかについて簡単に説明しました。
いつものように、例のコードはGitHubでから入手できます。