AJAXでCHATを作る場合のパターンについて、現時点で集めた情報をまとめてみました。
前提
JavaScript は、HTTP通信オブジェクトである XMLHttpRequest を持つためにブラウザの再読込なしにデータの送受信が可能です。また、JavaScriptはDOMを扱うことができるのでHTMLの要素を自由に書き換えることができます。(innerHTMLを用いても可) この二つの特徴を生かすと、ブラウザの再読込なしにHTTP経由で受け取ったデータをHTMLに表示することができます。
このような技術を用いて CHATを作ると、画面遷移(リロード)のない高速なCHATを構築することができます。あたかも IRCクライアントのようにWebページにログを書き出していくことができるわけです。これがAJAXでCHATを行う場合のメリットです。
ここまでは、AJAXでCHATを作るメリットについての前提条件です。紛らわしいので、以下、JavaScriptという記述はAJAXに統一します。
二つの方法
AJAX(XMLHttpRequest)を利用したCHATの実装方法は大きく分けて二つあります。
- ポーリング(polling)による実装
- Cometによる実装
ポーリングによるCHATの実装
ポーリングとは、一定間隔おきにサーバにリクエストを送り、データの変化を確認する処理を意味します。前世紀から存在する一般的なWEB CHATアプリケーションで行われていた処理と同じ考え方です。要するに"自動 F5 連打"ですね。
AJAXでポーリングを行うCHATを作る場合、下記のような内容が考えられます。
- 対象ファイルのサイズ(Content-Length)、更新日(Last-Modified)などの情報を取得して最新かどうかを確認する。この場合のリクエストはGETではなくHEADが望ましい
- 最新である場合にはデータを取得する。直近のデータサイズを保持しておき、If-Modified-Since+Rangeヘッダ付きのGETで差分のみを取得することが望ましい(Range: bytes=開始位置-終了位置)
これはログデータをファイルとして管理する前提なので、データベースを用いる場合には一工夫必要になります。ポーリングの問題点は、問い合わせの間隔が長いとリアルタイム性が失われる事、問い合わせの間隔が短いとサーバ側の負荷が極めて高くなることです。
これがどのぐらい深刻な問題かと言うと、1000ユーザが同時接続している状態で、1秒に1度の間隔でポーリングを行うと、計算してみるとわかりますが月間で26億ヒットとなり、「なにもせずブラウザを開いているだけで」グーグルのページビュー(12億)を超えるアクセス(!!)が殺到することになります。
http://blog.japan.cnet.com/kenn/archives/003149.html
要するにたいていの AJAX は Asynchronous と言いつつも1秒おきだのでポーリングしてるわけですね。
http://d.hatena.ne.jp/shinichiro_h/20060409#1144563467
ということで、AJAXといいながら Asynchronous = 非同期通信 ではなかったりもします。
Cometによる実装
Cometとは、AJAXと同じように規格の名称ではなく特定の技術の利用方法についての名前です。はWeb アプリケーションでサーバプッシュを実装する場合の総称、と言えるようです。プッシュ技術ではクライアントからサーバにリクエストするのではなく、サーバからデータを送信するという考え方であるため、ポーリングすることなくリアルタイムにデータを取得することができます。Cometは擬似的にこのサーバプッシュを実現します。
Cometについては引用が手っ取り早いのでさくっと引用します。
Cometでは、まずブラウザ側があらかじめサーバに対してHTTPリクエストを発行しておき、サーバ側はそのリクエストに対してレスポンスを返さずにずっと掴んだままにしておきます。そして、別の経路でサーバがキック(メッセージを送信)されたら、それまで掴みっぱなしになっていた複数のリクエストに対して一斉にメッセージを乗せてレスポンスを返すことで、擬似的にサーバからのプッシュを実現するのです。そして、ブラウザはすぐさまリクエストを再発行してふたたび応答待ちの状態へと戻る。まさに逆転の発想なのです。
http://blog.japan.cnet.com/kenn/archives/003149.html
HTTPを使って、擬似的にサーバからのプッシュを実現する技術。
サーバやネットワークへの負荷を抑えつつ、情報の更新をすぐにブラウザに反映させることができる。
基本的な仕組みは以下の通り。
- ブラウザ側があらかじめHTTPリクエストを発行しておく。
- サーバは、そのリクエストにすぐにレスポンスを返さずに保持しておく。
- サーバで何かイベント(情報の更新など)が起きたら、保持していたリクエストに対して(新しい情報を含む)レスポンスを返す。
- ブラウザ側はレスポンスを処理し、すぐに次のリクエストを発行する。
- これを繰り返す。
http://d.hatena.ne.jp/keyword/Comet
Cometの実装にはいくつかの種類があります。
long-pollについても引用します。
クライアントからの要求をうけたサーバは、HTTPリクエストを一時停止(suspend)し、タイムアウトか非同期イベントが発生した後に、再開(resume)し、クライアントに応答します。HTTPリクエストを一時停止している間、クライアントはサーバからの応答を待つことになります。
サーバからの応答があった時点で、ポーリングのように再び要求を出すことで、HTTPコネクションをつかみっぱなしにしている状態を仮想化します
http://www.panet.co.jp/report/long_pollPush.htm
HTTP コネクションを永続的に張り続けるのではなく、サーバからレスポンスがあった場合にコネクションを張り直すものをlong-pollと呼ぶようです(?)。が、Comet = long-pollのように書かれているものもありちょっと混乱します。いずれは同義になるのかもしれません。おそらくlong-pollがCometの最も一般的な実装なのではないかと思いますが、そもそも現時点でCometが一般的ではありません。
非表示にしたiframe要素に、元ページ(parent)にアクセスするscriptタグを徐々に出力することでサーバプッシュを実現する実装。多分この辺に書いてあると思いますが、あんまり興味ないので読んでいません。この方法だとブラウザはずっと読み込み中になるとのことです。
- multipart/x-mixed-replaceによる実装
multipart/x-mixed-replaceによる実装はFirefox依存らしいので調べていません。
※上記の三つの実装はWEB+DB PRESS Vol.34 の館野氏の記事で紹介されています。
イベントドリブン
Cometはサーバプッシュにてリアルタイム性を実現する技術であるため、サーバにデータ追加等の変化が発生した場合にクライアントに通知するような実装を行う必要があります。このように、ユーザや他のプログラムが実行した操作に対応して処理を行なうプログラムの実行形式をイベントドリブンと呼ぶようです。これには下記の種類があります。
- POE/Twisted/Jetty/dRuby/mpm_event_module
- SIGUSR1を使った実装
LingrはJettyを用いた実装、AJA CHAT(解説)はSIGUSR1をトラップする実装らしいです。
まだ調べてきれてはいませんが取りあえずのまとめでした。
※参考
・日経ソフトウエア 2007年1月号 Comet――プッシュ型のWebアプリケーションを作る 結城浩
・WEB+DB PRESS Vol.34 Comet - サーバプッシュ型Ajax 館野祐一
・Ajax 実装のための基礎テクニック
・多数のブログ