この記事は,日経ソフトウエア2007年1月号,連載「簡単実装で学ぶWeb技術2006」の第7回「Comet――プッシュ型のWebアプリケーションを作る」の再録です。記事は執筆時の情報に基づいており,現在では異なる場合があります。

結城 浩(ゆうき ひろし)
1963年生まれ。仕事&趣味は「プログラム書き&文章書き」。JavaやPerlの入門書で人気の著者。Webサイトはhttp://www.hyuki.com/

 こんにちは,結城浩です。

 今回は,サーバーからデータをプッシュするタイプのWebアプリケーションを作成する技法の「Comet」を紹介します。簡単なチャット・プログラムを通してCometの仕組みを学びましょう。

Cometとは

 通常のWebアプリケーションは,クライアント(Webブラウザ)から送られてくるリクエストを受け取って動作します。つまり,WebアプリケーションはユーザーがWeb上のリンクをたどったり,ボタンを押したりすることをきっかけに動くのです。ここで,動きの主導権はクライアント側にあります。クライアントがサーバーから情報を「引き出す」ことによって動作するので,通常のWebアプリケーションはプル(pull)型と呼ぶことがあります。ユーザーがサーバーの情報を「引き出す」イメージですね。

 ここで紹介するCometは,プル型と逆のプッシュ(push)型のWebアプリケーションを作る技術の一種です。プッシュ型のWebアプリケーションでは,サーバーがクライアント側に情報を「押し込む」動作になるのです。

 …と,これでは話が抽象的なので,Webチャットのアプリケーションを例にとって,アプリケーションを構成するパターンを三つ紹介していきます。現在利用されている技術と,ここで紹介するCometの違いを理解してください。

パターン1●AjaxやCometを利用しない場合

 図1は,AjaxやCometを利用しないシンプルなWebチャットの構成図です*1。ブラウザは,サーバーにアクセスして「現在のチャット・ログ」を取得します。これはクライアント側主導でサーバーから情報を引き出しているので「プル型」です。

図1●ポーリングを行う場合
図1●ポーリングを行う場合

 複数の人が同じサーバーにアクセスしてチャットしている場合,最新のチャット・ログを得るために,ブラウザは定期的にサーバーにアクセスする必要があります。この,「定期的にサーバーにアクセスして最新情報を得る」方法を,ポーリング(polling)と呼びます。

 たしかにこの方法で,チャットをWebアプリケーションとして実装できます。ただし,サーバーの情報が更新されていてもいなくても,ブラウザが定期的にサーバーにアクセスする必要があります。言い換えれば,ユーザーが多くなってくると,サーバーへのアクセス数が非常に多くなってしまいます。

パターン2●Ajaxを利用する場合

 図2はAjaxを使ったWebチャットの構成図です。クライアント側からサーバーへ,2種類のアクセスを行っています。

図2●JavaScriptでポーリングを行う場合
図2●JavaScriptでポーリングを行う場合

 まず,ブラウザからサーバーへアクセスします。このアクセスはチャットを利用する最初の1回だけで,チャットをするページが返されます。これが一つ目のアクセスです。二つ目は,ブラウザからではなくJavaScriptから行われるアクセスです。JavaScriptがサーバーにアクセスし,チャット・ログを表示するために必要な情報を得ます。この情報をJavaScriptで画面に反映します。

 これは,ブラウザの画面の遷移に同期しないで,サーバーのデータを取得して画面を書き換える――いわゆるAjax的なアプローチになります。ただし,クライアント側主導でサーバーから情報を引き出していることはパターン1と変わりません。パターン2もプル型です。

 最新のチャット・ログを画面に表示するためには,図1のときと同じように,JavaScriptは定期的にサーバーにアクセスしなくてはなりません。つまりポーリングを行う必要があるのです。

パターン3●Cometを利用する場合

 図3はCometを使ったWebチャットの構成図です。クライアント側からサーバーへは3種類のアクセスを行っています。

図3●Cometでプッシュする場合
図3●Cometでプッシュする場合

 一つ目は先ほどと同じく,ブラウザからサーバーへのアクセスです。

 二つ目は,「読み込み用のJavaScript」からサーバーへのアクセスです。このアクセスはHTTPを使うのですが,その振る舞いは変わっています。「読み込み用のJavaScript」からのリクエストを受け取ったサーバーは,すぐにはレスポンスを返さないのです。つまり,わざとクライアント側を「待たせて」おきます。クライアント側は「誰かがチャットに書き込む」というイベントを待つのです。

 三つ目のアクセスは,「書き込み用のJavaScript」からサーバーへのアクセスです。このアクセスはユーザーがチャットに対して発言(書き込み)したときに起こります。サーバー側は「書き込み用のJavaScript」からのリクエストを受け取ると,そのことを,待たせておいたスレッドやプロセスに通知します。この通知は「いま書き込みがあったので,チャット・ログが更新されていますよ」という意味になります。

 通知を受けたスレッドやプロセスは,「読み込み用のJavaScript」に対してレスポンスを返します。このレスポンスは,通常のHTTPレスポンスと何も変わりません。しかし,「サーバーに書き込まれたタイミングで返される」わけですから,サーバー主導で,情報がクライアント側に送られてきたと見なすことができます。つまり,これは(疑似的に)プッシュ型の通信を作り上げていることになるのです。

 プッシュ型の通信ではありますが,特殊なソフトウエアを使っているわけではありませんね。「WebブラウザとJavaScript」という既存のソフトウエアだけで動きます。Cometは既存のWebアプリケーション動作環境の範囲内で,プッシュ型のWebアプリケーションを実現していることになります。

 Cometを使ったチャットでは,クライアントは定期的にサーバーへアクセスする必要はありません。つまり,Cometを使うとポーリングは不要になります。「読み込み用のJavaScript」は,サーバーからのレスポンスを受け取った後,再度リクエストを発行します(そして待たされます)。しかしそれはあくまでサーバーに書き込みがあったときだけです。サーバーに更新がないときには,クライアントからサーバーにアクセスは発生しません。

Cometの良い点,悪い点

 前節で見たように,Cometではポーリングが発生しなくなります。したがって,サーバーへのアクセス数を減らすことができます。また,サーバーに書き込みがあったタイミングで情報が送られてくるので,リアルタイム性も向上することになります。これらはCometの良い点です。

 しかし,良い点ばかりでもありません。プッシュ型を実現するため,Cometではアプリケーションの動作中,「読み込み用のJavaScript」とサーバーとつながりっぱなしにしておく必要があります。これは,Cometの悪い点です。なぜなら,つながりっぱなしではサーバーの資源を確保し続けてしまうからです。この問題を解決し,Cometを有効に使うためには,これまでとは異なる要求を満たすWebサーバーが必要になってきます。