リアルタイム Web はなぜそれほど重要なのでしょうか?私たちはリアルタイムの世界に住んでいます。そのため、Web の究極かつ最も自然な状態もリアルタイムである必要があります。ユーザーはリアルタイムの通信、データ、検索を必要としています。インターネット上のリアルタイム情報に対する要求もますます高まっており、情報やメッセージが数分遅れて更新されるのは耐えられません。現在、多くの大企業 (Google、Facebook、Twitter など) がリアルタイム Web に注目し、リアルタイム サービスを提供し始めています。リアルタイム Web は、将来最も注目されるトピックの 1 つになるでしょう。
リアルタイム Web の開発の歴史
従来の Web は HTTP リクエスト/レスポンス モデルに基づいています。クライアントは新しいページをリクエストし、サーバーはコンテンツをクライアントに送信し、クライアントは-リクエスト別のページを送信します。その後、誰かが AJAX を提案しました。これにより、ページ エクスペリエンスがより「動的」になり、バックグラウンドでサーバーへのリクエストを開始できるようになりました。ただし、サーバーにクライアントにプッシュする必要があるデータがさらにある場合、ページが読み込まれた後にサーバーからクライアントにデータを直接送信することはできません。リアルタイム データをクライアントに「プッシュ」することはできません。
この問題を解決するために、多くの解決策が提案されています。最も簡単な (強引な) 解決策は、ポーリングを使用することです。サーバーに時々新しいデータを要求します。これにより、ユーザーはアプリケーションがリアルタイムであるように感じられます。実際、サーバーは毎秒大量の接続リクエストを処理する必要があり、各リクエストには TCP スリーウェイ ハンドシェイクと HTTP ヘッダー情報が含まれるため、これにより遅延とパフォーマンスの問題が発生します。ポーリングは現在でも多くのアプリケーションで使用されていますが、最も理想的なソリューションではありません。
その後、Comet テクノロジーの導入により、さらに多くの高度なソリューションが登場しました。これらの技術ソリューションには、パーマネント フレーム (永久フレーム)、XHR ストリーム (xhr-multipart)、htmlfile、およびロング ポーリングが含まれます。ロングポーリングとは、クライアントがサーバーへの XHR 接続を開始することを意味します。この接続は決して閉じられず、クライアントの接続は常に一時停止されます。サーバーに新しいデータがあると、サーバーはすぐにクライアントに応答を送信し、接続を閉じます。その後、プロセス全体が繰り返され、このようにして「サーバー プッシュ」が達成されます。
Cometテクノロジーは非標準的なハッキングテクノロジーであるため、ブラウザの互換性が問題になります。まず、パフォーマンスの問題は解決できません。サーバーに対して開始されるすべての接続には完全な HTTP ヘッダー情報が含まれており、アプリケーションが非常に低い遅延を必要とする場合、これは厄介な問題になります。もちろん、Comet 自体に問題があるわけではありません。他の選択肢が見つかるまでは Comet が唯一の選択肢だからです。
サーバープッシュの実装にはブラウザプラグイン(Flashなど)やJavaも使用されます。 TCP に基づいてサーバーとのソケット接続を直接確立できるため、リアルタイム データをクライアントにプッシュするのに非常に適しています。問題は、すべてのブラウザーにこれらのプラグインがインストールされているわけではなく、特に企業ネットワークではファイアウォールによってブロックされることが多いことです。
今度はHTML5仕様が代替案を用意してくれました。ただし、この仕様は、多くのブラウザー、特に IE ではまだサポートされていないため、現在のところ、HTML5 WebSocket を実装するのが最善の方法です。コメットを使用します。
WebSocket の過去と現在
ご存知のとおり、Web アプリケーションの対話プロセスは通常、クライアントがブラウザーを介してリクエストを送信し、サーバーがリクエストを受信して処理し、結果をクライアントに返し、クライアントのブラウザーで行われます。このメカニズムは、情報の変更が特に頻繁ではないアプリケーションには許容されますが、特に業界でのモバイル インターネットの現在の急成長傾向の下では、高いリアルタイム要件と大規模な同時実行性を必要とするアプリケーションには不十分です。リアルタイムのユーザー応答は、金融証券に関するリアルタイム情報、Web ナビゲーション アプリケーションでの位置情報の取得、ソーシャル ネットワークでのリアルタイム メッセージ プッシュなど、Web アプリケーションが直面する一般的な問題です。
従来のリクエスト/レスポンス モデル Web 開発では、通常、このようなビジネス シナリオに対処するときにリアルタイム通信ソリューションが使用されます。
原理はシンプルで理解しやすいものです。つまり、クライアントは頻繁にリクエストします。一定の間隔でサーバーにリクエストを送信し、クライアントとサーバーのデータを同期させます。問題は明らかです。クライアントが固定の頻度でサーバーにリクエストを送信すると、サーバー上のデータが更新されず、多くの不要なリクエストが発生し、帯域幅が無駄になり、効率が低下します。
Flash に基づいて、AdobeFlash は独自の Socket 実装を通じてデータ交換を完了し、その後 Flash を使用して JavaScript 呼び出しに対応するインターフェイスを公開し、リアルタイム送信を実現します。この方法はポーリングよりも効率的であり、Flash はインストール率が高いため、幅広い適用シナリオがあります。ただし、モバイル インターネット端末での Flash のサポートは良好ではありません。 IOS システムには Flash はありませんが、Android では Flash がサポートされていますが、実際の使用効果は満足できるものではなく、モバイル デバイスのハードウェア構成要件は比較的高いです。 2012 年、Adobe は Android 4.1 以降のシステムのサポートを終了すると正式に発表し、モバイル端末における Flash の廃止を発表しました。
上記からわかるように、高い同時実行性とリアルタイムの要件に対処する場合、従来の Web モデルは克服できないボトルネックに遭遇します。データのリアルタイム送信を保証するには、効率的でエネルギーを節約する双方向通信メカニズムが必要です。 。これに関連して、Web TCP として知られる WebSocket が HTML5 仕様に基づいて登場しました。
初期の頃、HTML5 は業界で統一された仕様を形成していませんでした。さまざまなブラウザーやアプリケーション サーバーのメーカーが、IBM の MQTT、Comet オープン ソース フレームワークなど、さまざまな同様の実装を行っていました。2014 年まで、HTML5 は大手企業によって広く使用されていました。 IBM、Microsoft、Google などの推進と協力により、最終的にはその草案が実際の標準仕様に正式に実装され、WebSocket プロトコルも徐々に統一され始めました。 JavaEE7 で実装されているため、クライアントとサーバーの WebSocket の両方が完成しています。読者は HTML5 仕様を参照して、新しい HTML プロトコル仕様と WebSocket サポートを理解することができます。
WebSocketの仕組み
以下にWebSocketの原理と動作の仕組みを簡単に紹介します。
WebSocket は HTML5 の新しいプロトコルです。ブラウザとサーバー間の全二重通信を実現し、サーバーのリソースと帯域幅を節約し、リアルタイム通信を実現します。ただし、HTTP との最大の違いは次のとおりです。
WebSocket は、接続を確立した後、Socket と同様に、WebSocket サーバーとブラウザ/クライアント エージェントが相互にデータをアクティブに送受信できるようにするためのプロトコルです。ハンドシェイク接続では、接続が成功した後にのみ相互に通信できます。
非 WebSocket モードでの従来の HTTP クライアントとサーバー間の対話は次のとおりです。
図 1. 従来の HTTP リクエスト応答のクライアントとサーバーの対話図
WebSocket モードを使用したクライアントとサーバー間の対話は次のとおりです。以下に示します:
図 2. WebSocket リクエストとレスポンスのクライアントとサーバーの相互作用図
図を比較すると、各リクエストとレスポンスでクライアントがサーバーとの接続を確立する必要がある従来の HTTP と比較して、WebSocket はSocket に似た TCP ロングコネクション通信モードでは、WebSocket 接続が確立されると、以降のデータはフレーム列として送信されます。クライアントが WebSocket 接続を切断する前、またはサーバーが切断する前に、クライアントとサーバーが接続要求を再開始する必要はありません。クライアントとサーバー間の大量の同時実行と重い対話負荷トラフィックの場合、ネットワーク帯域幅リソースの消費が大幅に節約され、さらに、クライアントは同じ永続接続上でリアルタイムにメッセージを送受信できます。性的な利点は明らかです。
クライアントとサーバー間の対話型メッセージを通じて、WebSocket 通信と従来の HTTP の違いを見てみましょう:
クライアントでは、新しい WebSocket が新しい WebSocket クライアント オブジェクトをインスタンス化し、接続は ws:/ に似ています。 /yourdomain: ポート/パスのサーバー WebSocket URL。WebSocket クライアント オブジェクトはそれを自動的に解析して WebSocket リクエストとして識別し、それによってサーバー ポートに接続し、2 者間で送信されるデータの形式を実行します。 client も同様です:
リスト 1. WebSocket クライアント接続レポートのテキスト
GET /webfin/websocket/ HTTP/1.1 Host: localhost Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg== Origin: http://localhost:8080 Sec-WebSocket-Version: 13
クライアントによって開始された WebSocket 接続メッセージは、従来の HTTP メッセージと似ていることがわかります。「Upgrade: websocket」パラメータ値は、これが WebSocket タイプのリクエストであることを示しています。「Sec-WebSocket-Key」は、base64 エンコードです。 WebSocket クライアントによって送信される暗号文では、サーバーは対応する暗号化された「Sec-WebSocket-Accept」応答を返す必要があります。返さない場合、クライアントは「WebSocket ハンドシェイク中のエラー」エラーをスローして接続を閉じます。
メッセージの受信後にサーバーによって返されるデータ形式は次のとおりです。
リスト 2. WebSocket サーバーの応答メッセージ
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
「Sec-WebSocket-Accept」の値は、クライアントと同じキーを使用してサーバーによって計算されます。出力後、クライアントに返される「HTTP/1.1 101 Switching Protocols」は、サーバーが WebSocket プロトコルのクライアント接続を受け入れることを示します。このような要求と応答の処理の後、クライアント サーバーの WebSocket 接続ハンドシェイクが成功し、その後の TCP 通信が可能になります。が行われます。読者は、WebSocket プロトコル スタックを参照して、WebSocket クライアントとサーバー間の対話型データ形式の詳細を学ぶことができます。
開発に関しては、WebSocket API も非常にシンプルです。WebSocket をインスタンス化して接続を作成するだけで、サーバーとクライアントは相互にメッセージを送信および応答できます。詳細は WebSocket で確認できます。 WebSocket API とコードの実装については、実装とケース分析のセクションを参照してください。
WebSocket の実装
前述したように、WebSocket の実装はクライアントとサーバーの 2 つの部分に分かれています。クライアント (通常はブラウザ) が WebSocket 接続リクエストを発行し、サーバーがそれに応答して TCP ハンドシェイクと同様のアクションを実装します。 HTTP の長い接続高速チャネルがブラウザ クライアントと WebSocket サーバーの間に形成されます。その後、両者の間で直接データが送信されるため、接続を開始して応答する必要がなくなります。
以下は、WebSocket サーバー API とクライアント API の簡単な説明です。
WebSocket サーバー API
WebSocket サーバーは、基本的に、JEE JSR356 標準仕様 API に準拠するさまざまな主流のアプリケーション サーバー メーカーによってサポートされています (詳細については、JSR356 WebSocket API 仕様を参照してください)。以下に、一般的な商用アプリケーション サーバーとオープン ソース アプリケーション サーバーのペアをいくつか示します。 WebSocket サーバーのサポート:
表 1. WebSocket サーバーのサポート
メーカー IBM WebSphere は、WebSphere 8.0 以降でサポートされており、7.X より前のバージョンでは、MQTT と組み合わせた同様の HTTP がサポートされています。 Oracle WebLogic 12cサポート、11Gおよび10Gバージョンは、HTTPパブリッシュを介して同様のHTTPロング接続をサポートしていますMICROSOFTIIS 7.0+サポートの による の 100 への道のり -- プレーン Java を変換できる API オブジェクト (POJO) は、WebSocket サーバーのエンドポイントとして @ServerEndpoint アノテーションを使用します。 コード例は次のとおりです。サーバー API の例
@ServerEndpoint("/echo") public class EchoEndpoint { @OnOpen public void onOpen(Session session) throws IOException { //以下代码省略... } @OnMessage public String onMessage(String message) { //以下代码省略... } @Message(maxMessageSize=6) public void receiveMessage(String s) { //以下代码省略... } @OnError public void onError(Throwable t) { //以下代码省略... } @OnClose public void onClose(Session session, CloseReason reason) { //以下代码省略... } }
コードの説明:
上記の単純なコードは、@ServerEndpoint("/echo") のアノテーション アノテーション エンドポイントは、WebSocket サーバーが ws://[サーバー IP またはドメイン] で実行されていることを示します。 name]:[サーバー ポート]/websockets/echo アクセス エンドポイントにアクセスすると、WebSocket クライアント API はすでに HTTP 永続接続を開始できます。
ServerEndpoint のアノテーションが付けられたクラスには、パラメーターのないパブリック コンストラクターが必要です。 @onMessage のアノテーションが付けられた Java メソッドは、受信 WebSocket 情報をテキスト形式またはバイナリ形式で受け取るために使用されます。
OnOpen 在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session 表明两个 WebSocket 端点对话连接的另一端,可以理解为类似 HTTPSession 的概念。
OnClose 在连接被终止时调用。参数 closeReason 可封装更多细节,如为什么一个 WebSocket 连接关闭。
更高级的定制如 @Message 注释,MaxMessageSize 属性可以被用来定义消息字节最大限制,在示例程序中,如果超过 6 个字节的信息被接收,就报告错误和连接关闭。
注意:早期不同应用服务器支持的 WebSocket 方式不尽相同,即使同一厂商,不同版本也有细微差别,如 Tomcat 服务器 7.0.5 以上的版本都是标准 JSR356 规范实现,而 7.0.2x/7.0.3X 的版本使用自定义 API (WebSocketServlet 和 StreamInbound, 前者是一个容器,用来初始化 WebSocket 环境;后者是用来具体处理 WebSocket 请求和响应,详见案例分析部分),且 Tomcat7.0.3x 与 7.0.2x 的 createWebSocketInbound 方法的定义不同,增加了一个 HttpServletRequest 参数,使得可以从 request 参数中获取更多 WebSocket 客户端的信息,如下代码所示:
清单 4.Tomcat7.0.3X 版本 WebSocket API
public class EchoServlet extends WebSocketServlet { @Override protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) { //以下代码省略.... return new MessageInbound() { //以下代码省略.... } protected void onBinaryMessage(ByteBuffer buffer) throws IOException { //以下代码省略... } protected void onTextMessage(CharBuffer buffer) throws IOException { getWsOutbound().writeTextMessage(buffer); //以下代码省略... } }; } }
因此选择 WebSocket 的 Server 端重点需要选择其版本,通常情况下,更新的版本对 WebSocket 的支持是标准 JSR 规范 API,但也要考虑开发易用性及老版本程序移植性等方面的问题,如下文所述的客户案例,就是因为客户要求统一应用服务器版本所以使用的 Tomcat 7.0.3X 版本的 WebSocketServlet 实现,而不是 JSR356 的 @ServerEndpoint 注释端点。
WebSocket 客户端 API
对于 WebSocket 客户端,主流的浏览器(包括 PC 和移动终端)现已都支持标准的 HTML5 的 WebSocket API,这意味着客户端的 WebSocket JavaScirpt 脚本具备良好的一致性和跨平台特性,以下列举了常见的浏览器厂商对 WebSocket 的支持情况:
表 2.WebSocket 客户端支持
浏览器 支持情况
Chrome Chrome version 4+支持
Firefox Firefox version 5+支持
IE IE version 10+支持
Safari IOS 5+支持
Android Brower Android 4.5+支持
客户端 WebSocket API 基本上已经在各个主流浏览器厂商中实现了统一,因此使用标准 HTML5 定义的 WebSocket 客户端的 JavaScript API 即可,当然也可以使用业界满足 WebSocket 标准规范的开源框架,如 Socket.io。
以下以一段代码示例说明 WebSocket 的客户端实现:
清单 5.WebSocket 客户端 API 示例
var ws = new WebSocket(“ws://echo.websocket.org”); ws.onopen = function(){ws.send(“Test!”); }; ws.onmessage = function(evt){console.log(evt.data);ws.close();}; ws.onclose = function(evt){console.log(“WebSocketClosed!”);}; ws.onerror = function(evt){console.log(“WebSocketError!”);};
第一行代码是在申请一个 WebSocket 对象,参数是需要连接的服务器端的地址,同 HTTP 协议开头一样,WebSocket 协议的 URL 使用 ws://开头,另外安全的 WebSocket 协议使用 wss://开头。
第二行到第五行为 WebSocket 对象注册消息的处理函数,WebSocket 对象一共支持四个消息 onopen, onmessage, onclose 和 onerror,有了这 4 个事件,我们就可以很容易很轻松的驾驭 WebSocket。
ブラウザと WebSocketServer が正常に接続されると、onopen メッセージがトリガーされます。接続が失敗した場合、データの送信または受信が失敗した場合、またはデータの処理中にエラーが発生した場合は、ブラウザが受信したときに onerror メッセージがトリガーされます。 WebSocketServer によって送信されたデータ、onmessage がトリガーされるメッセージ、パラメータ evt にはサーバーによって送信されたデータが含まれます。WebSocketServer によって送信された接続終了要求をブラウザが受信すると、onclose メッセージがトリガーされます。すべての操作が非同期コールバックを使用してトリガーされていることがわかります。これにより、UI がブロックされず、応答時間が短縮され、より優れたユーザー エクスペリエンスが提供されます。