リバース Ajax: 第 2 回 WebSocket

強力なソリューション (サーバーの制約を回避する)

リバース Ajax の手法によってイベント駆動型の Web アプリケーションを開発する方法を探るこの連載では、第 1 回でリバース Ajax 通信を実装する方法として、ポーリング、ピギーバック、そしてロング・ポーリングとストリーミングを使用した Comet を紹介しました。今回の記事では、WebSocket という新しい HTML5 API を使用してリバース Ajax を実装する方法を学びます。WebSocket は、ブラウザーのベンダーがネイティブに実装することも、隠し Flash コンポーネントに呼び出しを委任する FlashSocket というブリッジを使用して実装することもできます。さらに、この記事ではリバース Ajax 手法でのサーバー・サイドの制約にも目を向けます。

Mathieu Carbou, Java Web Architect, Ovea

Mathieu Carbou photoMathieu Carbou は、Ovea の Java Web アーキテクト兼コンサルタントとしてサービスおよび開発ソリューションを提供しています。彼は複数のオープンソース・プロジェクトでコミッターやリーダーを務め、講演を行うとともに、モントリオールの Java ユーザー・グループのリーダーとしても活躍しています。コード設計およびベスト・プラクティスに経験を積んでいる彼は、クライアント・サイドからバックエンドに至るまでのイベント駆動型 Web 開発のスペシャリストです。極めてスケーラブルな Web アプリケーションでのイベント駆動型およびメッセージング・ソリューションを提供することに専念しています。彼のブログを読んでください。



2011年 9月 02日

はじめに

私たちは最近では、高速で動的なアプリケーションに Web からアクセスすることを期待します。リバース Ajax 手法を適用したイベント駆動型 Web アプリケーションの開発方法を紹介するこの連載では、第 1 回でリバース Ajax、ポーリング、ストリーミング、Comet、そしてロング・ポーリングを紹介しました。そのなかで学んだように、リバース Ajax を確実に実装するには、今やすべてのブラウザーがサポートしている HTTP ロング・ポーリングを使用した Comet が最善の手段となります。

今回は、WebSocket を使用してリバース Ajax を実装する方法を学んでください。この記事では、サンプル・コードを使用して、WebSocket、FlashSocket、サーバー・サイドの制約、リクエスト・スコープ・サービス、そして長時間存続するリクエストを一時停止する方法を説明します。記事で使用するソース・コードはダウンロードすることができます。

前提条件

この記事を最大限利用するには、JavaScript および Java の知識があると理想的です。記事のサンプル・コードは、Java で作成された依存性注入フレームワークである Google Guice を使って作成されています。記事の内容を理解するには、依存性注入フレームワーク (Guice、Spring、Pico など) の概念を理解している必要があります。

記事のサンプルを実行するには、最新バージョンの Maven および JDK も必要です (「参考文献」を参照)。


WebSocket

HTML5 に登場した WebSocket は、Comet よりもずっと新しいリバース Ajax 手法です。双方向の全二重通信チャネルを可能にする WebSocket は、すでに多くのブラウザー (Firefox、Google Chrome、および Safari) でサポートされています。特殊なヘッダーを使った「WebSocket ハンドシェーク」と呼ばれる HTTP リクエストによって接続をオープンすると、接続がそのまま維持され、TCP ソケットそのものを使っているかのように、JavaScript でデータを作成および受信することができます。

WebSocket URL を開始するには、(SSL で) ws:// または wss:// と入力します。

図 1 に示すタイムラインでは、WebSocket を使用した通信を示します。特定のヘッダーを持つ HTTP ハンドシェークがサーバーに送信されると、サーバー・サイドまたはクライアント・サイドで、ある種のソケットを JavaScript で使用できるようになります。このソケットを使用することで、イベント・ハンドラーから非同期でデータを受信できるようになります。

図 1. WebSocket によるリバース Ajax
WebSocket によるリバース Ajax

この記事のソース・コードのダウンロードに、サンプル WebSocket が含まれています。このサンプルを実行すると、リスト 1 のような出力が表示されるはずです。リスト 1 の出力からは、サーバー・サイドでイベントが発生したのと同時に、そのイベントがクライアント・サイドに表示される様子が見て取れます。クライアントが何らかのデータを送信すると、サーバーはそれをクライアントにエコー・バックします。

リスト 1. JavaScript での WebSocket の例
[client] WebSocket connection opened 
[server] 1 events 
[event] ClientID = 0 
[server] 1 events 
[event] At Fri Jun 17 21:12:01 EDT 2011 
[server] 1 events 
[event] From 0 : qqq 
[server] 1 events 
[event] At Fri Jun 17 21:12:05 EDT 2011 
[server] 1 events 
[event] From 0 : vv

通常、JavaScript ではリスト 2 に示すように WebSocket を使用します (ブラウザーが WebSocket をサポートする場合)。

リスト 2. JavaScript クライアント・コード
var ws = new WebSocket('ws://127.0.0.1:8080/async'); 
ws.onopen = function() { 
    // called when connection is opened 
}; 
ws.onerror = function(e) { 
    // called in case of error, when connection is broken in example 
}; 
ws.onclose = function() { 
    // called when connexion is closed 
}; 
ws.onmessage = function(msg) { 
    // called when the server sends a message to the client. 
    // msg.data contains the message. 
}; 
// Here is how to send some data to the server 
ws.send('some data'); 
// To close the socket:
ws.close();

送受信するデータは、タイプを問いません。WebSocket は TCP ソケットのようなものなので、どのタイプのデータを送信するかは、クライアントとサーバー次第です。この例では、JSON ストリングを送信しています。

ブラウザーのコンソール (または Firebug) でハンドシェークの HTTP リクエストを詳しく調べると、JavaScript の WebSocket オブジェクトが作成される場合には、WebSocket 固有のヘッダーがあることに気付くはずです。リスト 3 に一例を記載します。

リスト 3. HTTP リクエスト・ヘッダーおよびレスポンス・ヘッダーの例
Request URL:ws://127.0.0.1:8080/async 
Request Method:GET 
Status Code:101 WebSocket Protocol Handshake 

Request Headers 
Connection:Upgrade 
Host:127.0.0.1:8080 
Origin:http://localhost:8080 
Sec-WebSocket-Key1:1 &1~ 33188Yd]r8dp W75q 
Sec-WebSocket-Key2:1   7;    229 *043M 8 
Upgrade:WebSocket 
(Key3):B4:BB:20:37:45:3F:BC:C7 

Response Headers 
Connection:Upgrade 
Sec-WebSocket-Location:ws://127.0.0.1:8080/async 
Sec-WebSocket-Origin:http://localhost:8080 
Upgrade:WebSocket 
(Challenge Response):AC:23:A5:7E:5D:E5:04:6A:B5:F8:CC:E7:AB:6D:1A:39

WebSocket ハンドシェークは上記のヘッダーのすべてを使って、長時間存続する接続を許可し、接続をセットアップします。WebSocket JavaScript オブジェクトにも、以下の 2 つの有用なプロパティーが含まれます。

ws.url
WebSocket サーバーの URL を返します。
ws.readyState
現在の接続状態を示す、以下の値を返します。
  • CONNECTING (接続中) = 0
  • OPEN (オープン) = 1
  • CLOSED (クローズ) = 2

サーバー・サイドでの WebSocket の処理は、もう少し複雑になってきます。WebSocket を標準的な方法でサポートするための Java 仕様は (今の時点ではまだ) ありません。Web コンテナー (Tomcat や Jetty など) のWebSocket 機能を使用するには、その WebSocket 機能へのアクセスを可能にするコンテナー固有のライブラリーにアプリケーション・コードを密接に結合する必要があります。

私たちが使用しているのは Jetty コンテナーなので、サンプル・コードの websocket フォルダーにあるサンプルでは Jetty の WebSocket API を使用しています。リスト 4 に、この場合の WebSocket ハンドラーを記載します (連載の第 3 回では、これとは別のバックエンド WebSocket API を使用します)

リスト 4. Jetty コンテナーの場合の WebSocket ハンドラー
public final class ReverseAjaxServlet extends WebSocketServlet { 
    @Override 
    protected WebSocket doWebSocketConnect(HttpServletRequest request,
                                           String protocol) { 
        return [...] 
    } 
}

Jetty で WebSocket ハンドシェークを処理するには、いくつかの方法があります。そのうち最も簡単なのは、Jetty の WebSocketServlet をサブクラス化して doWebSocketConnect メソッドを実装するという方法です。このメソッドは、Jetty の WebSocket インターフェースのインスタンスを返すように求めます。そのため、このインターフェースを実装し、WebSocket 接続を表すエンドポイントのようなものを返す必要があります。リスト 5 に一例を記載します。

リスト 5. WebSocket の実装例
class Endpoint implements WebSocket { 

    Outbound outbound; 

    @Override 
    public void onConnect(Outbound outbound) { 
        this.outbound = outbound;    
    } 

    @Override 
    public void onMessage(byte opcode, String data) { 
        // called when a message is received 
        // you usually use this method 
    } 

    @Override 
    public void onFragment(boolean more, byte opcode, 
                           byte[] data, int offset, int length) { 
        // when a fragment is completed, onMessage is called. 
        // Usually leave this method empty. 
    } 

    @Override 
    public void onMessage(byte opcode, byte[] data, 
                          int offset, int length) { 
        onMessage(opcode, new String(data, offset, length)); 
    } 

    @Override 
    public void onDisconnect() { 
        outbound = null; 
    } 
}

クライアントにメッセージを送信するには、リスト 6 のアウトバンドを作成します。

リスト 6. クライアントへのメッセージの送信
if (outbound != null && outbound.isOpen()) {
    outbound.sendMessage('Hello World !');
}

クライアントを切断して WebSocket 接続を閉じるには、outbound.disconnect(); を使用します。

WebSocket は、レイテンシーのない双方向通信を実装する非常に強力な手段であり、Firefox、Google Chrome、Opera の他、最近のいくつかのブラウザーでサポートされています。jWebSocket の Web サイトによると、ブラウザーでのサポート状況は以下のようになっています。

  • Chrome には 4.0.249 以降、ネイティブ WebSocket が組み込まれています。
  • Safari 5.x にはネイティブ WebSocket が組み込まれています。
  • Firefox 3.7a6 および 4.0b1+ にはネイティブ WebSocket が組み込まれています。
  • Opera には 10.7.9067 以降、ネイティブ WebSocket が組み込まれています。

jWebSocket についての詳細は、「参考文献」を参照してください。

利点

WebSocket は、強力な双方向通信、低レイテンシーを実現し、エラーも簡単に処理することができます。Comet のロング・ポーリングのように接続が多数存在するわけではなく、また Comet のストリーミングに見られるような欠点もありません。Comet では再接続、タイムアウト、Ajax リクエスト、確認応答、さらにオプションで異なるトランスポート (Ajax ロング・ポーリングまたは jsonp ポーリング) を処理する上で有効なライブラリーが必要になりますが、それに比べ、この WebSocket の API は他の層を追加せずに、そのままとても簡単に使用することができます。

欠点

WebSocket には、以下の欠点があります。

  • HTML5 からの新しい仕様であるため、すべてのブラウザーでサポートされているわけではありません。
  • リクエスト・スコープがありません。WebSocket は TCP ソケットであり、HTTP リクエストではないため、Hibernate の SessionInViewFilter のようなリクエスト・スコープ・サービスを簡単に使用することができません。Hibernate は、HTTP リクエストをラップするフィルターを提供するパーシスタンス・フレームワークです。リクエストが開始されると、リクエスト・スレッドにバインドされたコンテキスト (トランザクションと JDBC 接続が含まれます) をセットアップします。リクエストが完了すると、フィルターがこのコンテキストを破棄します。

FlashSocket

WebSocket をサポートしていないブラウザー用に、一部のライブラリーは FlashSocket (Flash を使用したソケット) にフォールバックする機能を備えています。一般に、これらのライブラリーは同じ正式な WebSocket API を提供していますが、その実装方法としては、Web サイトに組み込まれた隠し Flash コンポーネントに呼び出しを委任します。

利点

FlashSocket は、トランスペアレントに WebSocket 機能を提供します。それは、HTML5 WebSocket をサポートしていないブラウザーの場合でも例外ではありません。

欠点

FlashSocket には以下の欠点があります。

  • Flash プラグインをインストールする必要があります (通常、このプラグインはすべてのブラウザーにインストールされています)。
  • ファイアウォールのポート 843 をオープンし、Flash コンポーネントが HTTP リクエストによって、ドメイン許可の含まれるポリシー・ファイルを取得できるようにしなければなりません。

    ポート 843 に到達できない場合には、ライブラリーがフォールバックするか、エラーを返します。この処理には時間がかかるため (最大 3 秒ですが、ライブラリーによって異なります)、Web サイトの速度が低下します。

  • クライアントがプロキシー・サーバーの背後にある場合には、ポート 843 への接続が拒否される可能性があります。

ブリッジを提供するのは、WebSocketJS プロジェクトです。このプロジェクトでは、少なくとも Flash 10 がインストールされていることを要件とし、それによって WebSocket のサポートを Firefox 3、Internet Explorer 8、および Internet Explorer 9 に提供します。

推奨事項

WebSocket は、Comet よりも多くのメリットをもたらします。日常的な開発作業では、WebSocket をサポートするクライアントのほうが速度に優れ、生成されるリクエストの数も少なくなります (そのため、帯域幅の使用量も少なくなります)。ただし、すべてのブラウザーが WebSocket をサポートしているわけではありません。そこで、最適なリバース Ajax ライブラリーとなるのが、WebSocket のサポートを検出する機能を備え、WebSocket がサポートされていない場合には Comet (ロング・ポーリング) にフォールバックできるライブラリーです。

あらゆるブラウザーを最大限に活用するとともに、互換性を維持するには、2 つの手法が必要になります。そのため、これらの手法に対して抽象化層を提供するクライアント JavaScript ライブラリーを使用することをお勧めします。連載の第 3 回と第 4 回では、これらのライブラリーについて詳しく探り、第 5 回でそれぞれのアプリケーションを紹介します。サーバー・サイドでは、次のセクションで説明するように、事態は少し複雑です。


サーバー・サイドでのリバース Ajax の制約事項

クライアント・サイドで有効なリバース Ajax ソリューションの概要を説明したところで、今度はサーバー・サイドのリバース Ajax ソリューションについて見て行きます。これまでのサンプルでは、主にクライアント JavaScript コードを使用しました。サーバー・サイドでリバース Ajax 接続を受け入れるためには、お馴染みの短時間の HTTP リクエストよりも長く存続する接続を処理する特有の機能が必要になってきます。また、より適切にスケーリングするために、新しいスレッド化モデルを使用しなければなりません。それには、リクエストの一時停止を可能にする特定の Java API が必要です。さらに、WebSocket の場合、アプリケーションで使用されるサービスのスコープを正しく管理する必要も出てきます。

スレッド化とノンブロッキング I/O

通常、Web サーバーは着信 HTTP 接続ごとに 1 つのスレッド (1 つのプロセス) を関連付けます。この接続を保持する (キープアライブする) ことで、複数のリクエストが同じ接続を経由して行われるようになります。この記事のサンプルでは、この振る舞いを変更する mpm_fork または mpm_worker モデルを使用して、Apache Web サーバーを構成することができます。Java Web サーバー (アプリケーション・サーバーにしても同じことです) は一般に、着信する接続ごとに 1 つのスレッドを使用するからです。

新しいスレッドを生成すると、メモリーを消費し、リソースを無駄に使うことになります。というのも、生成されたスレッドが使用される保証はないためです。接続は有効だとしても、クライアントからも、サーバーからもデータが送信されてこない場合はあります。生成されたスレッドが使用されるか使用されないかに関わらず、このスレッドのスケジューリングとコンテキスト・スイッチによって、メモリーと CPU リソースが消費されます。さらに、スレッド化モデルを使用してサーバーを構成するときには、スレッド・プールを構成して、着信接続を処理するスレッドの最大数を設定しなければならないのが通常です。この最大数を誤って低すぎる値に設定すると、スレッド枯渇という問題に陥り、リクエストを処理するスレッドが使用可能になるまで、リクエストが待機状態になります。したがって、最大同時接続数に達すると、応答時間が遅くなります。その一方、高い値に設定した場合には、メモリー不足の例外が発生する可能性があります。過剰な数のスレッドを生成すると、JVM のヒープが使い果たされて、サーバーがクラッシュすることになりかねません。

Java では最近、ノンブロッキング I/O (NIO) という新しい I/O API を導入しました。この API が使用するセレクターによって、サーバーに対して新規 HTTP 接続が行われるごとに、接続にスレッドをバインドする必要がなくなります。データが送信されて、イベントを受信すると、リクエストを処理するためのスレッドが割り当てられることから、これは thread-per-request (リクエストごとのスレッド) モデルと呼ばれます。このモデルにより、WebSphere や Jetty などの Web サーバーは、ユーザー接続数が増えても、一定数のスレッドでこれらの接続を処理できるようになります。ハードウェア構成が同じ場合、thread-per-request モードで実行される Web サーバーのほうが、thread-per-connection (接続ごとのスレッド) モードで実行される Web サーバーより遥かにスケーラブルです。

Philip McCarthy 氏 (『Comet and Reverse Ajax』の著者) は彼のブログのなかで、この 2 つのスレッド化モデルのスケーラビリティーに関する興味深いベンチマークを取り上げています (「参考文献」にリンクを記載)。図 2 に示してあるのは、そのベンチマークと同じパターンです。つまり、接続数があまりにも多くなるとスレッド化モデルが機能しなくなるパターンです。

図 2. スレッド化モデルのベンチマーク
スレッド化モデルのベンチマーク

thread-per-connection モデル (図 2 の Threads) のほうが、一般的には応答時間に優れています。これは、すべてのスレッドが使用中の状態、使用可能な状態、または待機状態となっているからです。けれども、接続数が過剰に増えると、このモデルはサービスを停止します。一方、thread-per-request モデル (図 2 の Continuations) では、受信したリクエストに対応するためにスレッドを使用し、接続は NIO セレクターによって処理します。応答時間の点では多少劣るものの、スレッドが再利用されるため、接続数が多い場合には、このソリューションのほうがスケーラブルです。

スレッド化が裏でどのように機能するのかを理解するには、LEGO ブロックをセレクターに例えて考えてみてください。LEGO ブロックが接続を受信するたびに、それぞれの接続がピンによって識別されます。したがって、LEGO ブロック (セレクター) には、接続数と同じだけのピン (キー) があることになります。LEGO ブロックが新しいイベントの発生を待機する間、ピンを繰り返し処理するために必要なスレッドは 1 つだけです。イベントが発生すると、セレクター・スレッドは発生したイベントのキーを取得し、着信リクエストに対応するためにスレッドを使用できるようになります。

「Rox Java NIO Tutorial」に、Java で NIO を使用する好例が記載されています (「参考文献」を参照)。


リクエスト・スコープ・サービス

多くのフレームワークでは、サーブレットで受信した Web リクエストを処理するサービス (フィルター) を提供しています。例えば、フィルターは以下の処理を行います。

  • JDBC 接続をリクエスト・スレッドにバインドし、リクエスト全体で一貫して 1 つの接続だけが使用されるようにします。
  • リクエストの終了時に変更をコミットします。

Google Guice (依存性注入ライブラリー) の Guice Servlet 拡張機能も、このようなフィルターの一例です。Spring と同じように、Guice ではサービスをリクエスト・スコープにバインドすることができます。そのため、新しいリクエストごとに、インスタンスは 1 度しか作成されません (詳細については「参考文献」を参照)。

典型的な使用方法では、リポジトリー (例えばデータベース) から取得したユーザー・オブジェクトを、クラスター化された HTTP セッションから取得したユーザー ID を使ってリクエスト内にキャッシングすることになります。Google Guice では、リスト 7 のようなコードを使用することになります。

リスト 7. リクエスト・スコープのバインディング
@Provides 
@RequestScoped 
Member member(AuthManager authManager, 
              MemberRepository memberRepository) { 
    return memberRepository.findById(authManager.getCurrentUserId());
}

メンバーがクラスに注入されると、Guice はリクエストからメンバーを取得しようと試みます。見つからない場合には、リポジトリー呼び出しを実行して、その結果をリクエストに格納します。

リクエスト・スコープ・サービスは、WebSocket を除くすべてのリバース Ajax ソリューションで使用することができます。WebSocket 以外のソリューションはいずれも、短時間存続する HTTP リクエストか、長時間存続する HTTP リクエストのいずれかを使用します。そのため、各リクエストはサーブレット・ディスパッチ・システムを通り、そこでフィルターが実行されます。一時停止された (長時間存続する) HTTP リクエストの完了時には、この連載の今後の記事で説明するように、リクエストが再度フィルター・チェーンを通るようにする方法もあります。

WebSocket の場合、TCP ソケットでの場合と同じようにデータは onMessage コールバックで直接渡されます。このデータを渡すために HTTP リクエストは使われていないため、スコープ・オブジェクトを取得および格納する先となるリクエスト・コンテキストはありません。したがって、onMessage コールバックからスコープ・オブジェクトが返されてくることを要求するサービスは、WebSocket では使用できないというわけです。

ソース・コードのダウンロードに含まれるサンプル guice-and-websocket に、この制約を回避しながらも、onMessage コールバック内でリクエスト・スコープ・オブジェクトを使用する方法が示されています。サンプルを実行して、Web ページ上の各ボタンをクリックして Ajax 呼び出し (リクエスト・スコープ)、WebSocket 呼び出し、そしてリクエスト・スコープをシミュレートした WebSocket 呼び出しをテストすると、図 3 の結果が出力されます。

図 3. リクエスト・スコープ・サービスを使用した WebSocket ハンドラーの出力
リクエスト・スコープ・サービスを使用した WebSocket ハンドラーの出力

上記の問題は、以下のフレームワークまたはシステムを使用しているかどうかによって発生する場合があります。

  • Spring
  • Hibernate
  • リクエスト・スコープまたはリクエスト単位のモデル (OpenSessionInViewFilter など) を必要とするその他すべてのフレームワーク
  • ThreadLocal 機能を使用して、フィルター内で変数のスコープをリクエスト・スレッドに設定し、これらの変数に後からアクセスするシステム

Guice には、リスト 8 に示す簡潔な解決法があります。

リスト 8. WebSocket の onMessage コールバックからのリクエスト・スコープのシミュレーション
// The reference to the request is hold when the 
// doWebSocketMethod is called 
HttpServletRequest request = [...] 
Map<Key<?>, Object> bindings = new HashMap<Key<?>, Object>(); 
// I have a service which needs a request to get the session, 
// so I provide the request, but you could provide any other 
// binding that may be needed 
bindings.put(Key.get(HttpServletRequest.class), request); 
ServletScopes.scopeRequest(new Callable<Object>() { 
    @Override 
    public Object call() throws Exception { 
        // call your repository or any service using the scoped objects
        outbound.sendMessage([...]); 
        return null; 
    } 
}, bindings).call();

長時間存続するリクエストの一時停止

Comet には、もう 1 つ難題があります。それは、サーバーがパフォーマンスに影響を与えることなく、長時間存続するリクエストを一時停止し、サーバー・イベントを受信すると同時にリクエストを再開して完了できるようにするにはどうすればよいのでしょう?

明らかに、リクエストとレスポンスを保持するだけでは、スレッドの枯渇を引き起こし、メモリーを大量に消費することになってしまいます。ロング・ポーリングのリクエストをノンブロッキング I/O で一時停止するには、特定の API が必要です。Java では、Servlet 3.0 仕様がこのような API を提供しています (連載第 1 回を参照)。リスト 9 に一例を記載します。

リスト 9. Servlet 3.0 による非同期サーブレットの定義
<?xml version="1.0" encoding="UTF-8"?> 

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
         xmlns:j2ee="http://java.sun.com/xml/ns/javaee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml
/ns/j2ee/web-app_3.0.xsd"> 

    <servlet> 
        <servlet-name>events</servlet-name> 
        <servlet-class>ReverseAjaxServlet</servlet-class> 
        <async-supported>true</async-supported> 
    </servlet> 

    <servlet-mapping> 
        <servlet-name>events</servlet-name> 
        <url-pattern>/ajax</url-pattern> 
    </servlet-mapping> 

</web-app>

非同期サーブレッドを定義しておけば、Servlet 3.0 API を使ってリクエストを保留し、再開することができます (リスト 10 を参照)。

リスト 10. リクエストの保留および再開
AsyncContext asyncContext = req.startAsync(); 
// Hold the asyncContext reference somewhere

// Then when needed, in another thread you can resume or complete
HttpServletResponse req = 
    (HttpServletResponse) asyncContext.getResponse(); 
req.getWriter().write("data"); 
req.setContentType([...]); 
asyncContext.complete();

Servlet 3.0 より前は、各コンテナーが (今でもそうですが) 固有のメカニズムを持っていました。その有名な例は、Jetty の continuation (継続) です。Java の多くのリバース Ajax ライブラリーは、Jetty の continuation を利用しています。これは、致命的な問題というわけではなく、Jetty コンテナーでアプリケーションを実行しなければならないわけではありません。Servlet 3.0 API は、実行されているコンテナーを検出し、Tomcat や Grizzly などの別のコンテナーで実行している場合には、この API (使用可能な場合) にフォールバックするだけの賢さを持ち合わせています。これは Comet にも当てはまることですが、WebSocket を使用するとしたら、現在のところはコンテナー固有の機能を使用するしか選択肢はありません。

Servlet 3.0 仕様はまだリリースされていませんが、Servlet 3.0 API はリバース Ajax を実行する標準的な手段であることから、多数のコンテナーがすでにこの API を実装しています。


まとめ

WebSocket は非常に強力なリバース Ajax ソリューションです。ただし、いくつかの欠点もあります。それは、現時点では、すべてのブラウザーに実装されているわけではないこと、そしてリバース Ajax ライブラリーの助けがなければ、Java を使用するサーバー・サイドでは使いづらいことです。また、標準のリクエスト・レスポンス・スタイルを使用していないことから、フィルター・チェーンによってスコープ設定をすることはできません。Comet と WebSocket には、コンテナーのサーバー・サイドに固有の機能が必要です。最近のコンテナーを使用する場合には、この点を注意しておかないとスケーリングできないことがあります。

連載の第 3 回では、Comet および WebSocket に対応した別のサーバー・サイド API を詳しく探ります。さらに、リバース Ajax フレームワーク、Atmosphere についても説明するので、お見逃しなく。


ダウンロード

内容ファイル名サイズ
Article source codereverse_ajaxpt2_source.zip14KB

参考文献

学ぶために

製品や技術を入手するために

  • WebSocketJS (WebSocket Flash Bridge): この Flash を原動力とする HTML5 WebSocket 実装を入手してください。
  • Google Guice: Java 5 以降に対応した軽量の依存性注入フレームワーク、Google Guice を入手してください。
  • Jetty: WebSocket のサポートを備えた Web サーバー兼 javax.servlet コンテナー、Jetty を入手してください。
  • Apache Maven: ソフトウェア・プロジェクト管理および認識ツール、Maven を入手してください。
  • Java Development Kit バージョン 6: デスクトップとサーバー、そして最近必要とされる組み込み環境の Java アプリケーションを開発、デプロイするために使用できる Java SE (Java Platform, Standard Edition) を入手してください。
  • IBM のソフトウェアを無料で試してみてください。試用版をダウンロードすることも、オンライン評価版にログインすることも、Sandbox 環境で製品を操作することも、クラウドを介して IBM 製品にアクセスすることもできます。100 を超える IBM 製品の評価版のなかから選ぶことができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=754264
ArticleTitle=リバース Ajax: 第 2 回 WebSocket
publish-date=09022011