Ajax非同期通信アプリケーションのデバッグを行う際に、以下ライブラリを利用し、ログ出力を行ないながら動作詳細を確認した。フランス製。
<!-- 相対パスの指定をすること -->
<script language="JavaScript" src="cookie.js"></script>
<script language="JavaScript" src="Log4js.js"></script>
<script language="JavaScript">
/*
* 存在しないなら、ログウィンドウを作成する。
* @param psWindowName ログウィンドウのID。または空文字列('')。
* @param psPath js_popup.htmlへのパス。末尾にスラッシュ(/)をつけないこと。
*/
log4js = new Log4js('', '.');
</script>
/*
* ログを出力する。
* @param psFile ログ出力元ファイル。
* @param psFun ログ出力元機能。
* @param psTxt メッセージ。
* @param paToScan インスペクタ対象オブジェクト配列。オブジェクト名,オブジェクトの連続で指定。
*/
log4js.debug('exemple.html', 'body', 'ポーリング開始', []);
log4js.info('exemple.html', 'body', '読み込み中', [ 'document', document ]);
カテゴリ分けあり。デザインが美しい。
ダウンロードは、ページ内を「download」で検索すると見つかる。
DIVウィンドウにログを出力。カテゴリ分けあり。コマンドシェル付属。
シンプルで使いやすい。
シンプルで見やすいオブジェクトインスペクタ。コードがきれいで、ドキュメントも揃っている。
テストツールJsUniにも、トレース/ログ出力ライブラリが付属している。カテゴリ分けあり。出力シンプル。画面右上「Test Runner Online」から、実行を確認できる。
ブラウザ上で動く総合開発環境。コーディング、ワークフローエディタ、問題管理、ER図作成、コミュニケーション、本当に大体揃っている。
動きもデザインも不味いが、瑣末なことだ。
現在、RSSリーダー、メーラー、IME、ゲーム、音楽/映像再生、ワープロ、表計算、総合開発環境と、日常使うものは全てブラウザに乗っている。
OSや開発環境を、CDで持ち歩くようなナイーブな状況は考えられない。
これからはインターネットの時代だなー、と思う。
私にとってこれは、今使っているThinkpadが、最後に買ったパソコンになるということだ。
原文:Jetty 6.0 Continuations - AJAX Ready!
著者:Greg Wilkins
Jetty 6.0.0 alpha 3がリリースされました! サーブレット2.4のサーバが400kのjarと、たった140kの依存ライブラリに収まっています。(さらにJSPが必要な場合でも、たった2.6Mです!!)。
小さくて、速くて、簡潔で魅力的な点は全く損なわずに、スケーラブルなAJAXアプリケーションを構築するための、Continuationsと言う新機能をJetty6はサポートしました。Continuationsは、スレッドを使わずに非同期イベントの待機を実現します。
スケーラブルなサーブレットサーバを構築する上で、主要な課題の1つにスレッドとコネクションの取り扱いがあります。Javaの伝統的なIOモデルは、あらゆるTCP/IPコネクションにスレッドを関連づけます。このモデルは、非常にアクティブなスレッドが少しあれば、大量のリクエスト(/秒)を処理できます。
しかし、典型的なウェブアプリケーションのトラフィックプロファイルは、多くのユーザがページを読んだり次のリンクを捜していたりする間、ほぼ仕事をしていない持続的HTTPコネクションで構成されています。このようなプロファイルにおいて、大規模サイトでの「コネクション単位スレッド」モデルの採用は、何千人ものユーザをサポートするために、何千ものスレッドを必要とし問題となります。
この問題に対してNIOライブラリが役に立ちます。NIOライブラリは、リクエストを処理しているときだけスレッドをコネクションに割り当てることができますし、非同期なIOを使用することもできるためです。コネクションが、リクエストとリクエストの間に仕事をしていない場合、スレッドプールにスレッドを返すことができます。そしてNIOが、そのスレッドへ新しいリクエストを選び割り当てます。この「リクエスト単位スレッド」モデルは、サーバ全体の最大リクエスト(/秒)は減少しますが、非常に多くの接続(ユーザ)を処理することを可能にします。(Jetty 6では、最大リクエスト(/秒)の減少は、かなり改善されました。)*1
しかし、新たな問題があります。ウェブアプリケーションモデルとしてのAJAXの出現は、サーバサイドのトラフィックプロファイルを大きく変えています。AJAXサーバは非同期的なイベントをクライアントに渡すことができないため、AJAXクライアントはイベントをチェックするためにサーバをポーリングしなければなりません。頻繁なポーリングの繰り返しを避けるために、AJAXサーバが、イベントやタイムアウトが発生するまでポーリングリクエストを保留することがよくあります。つまり、アイドリングしているAJAXアプリケーションのサーバーには、非同期イベントが起きたらすぐにレスポンスをクライアントに送ることを目的として、保留されているリクエストがあるということです。これは素晴らしいテクニックですが、「リクエスト単位スレッド」モデルを台無しにしてしまいます。何故なら、そのような状態では、あらゆるクライアントがサーバへの未解決のリクエストを持つことになるためです。したがって、再びサーバは各クライアントあたり1つ以上のスレッドが必要となり、再び何千人ものユーザに同時に対応する問題に直面するのです。
解決策はContinuationです。これは、Jetty6で導入された新機能です。AJAXリクエストを処理するフィルターやサーブレットは、効率的なリクエストの一時停止と、現在のスレッドの解放をContinuationオブジェクトに要求できます。リクエストは、タイムアウトするか、Continuationオブジェクトのresumeメソッドが呼ばれるとすぐに再開されます。以下の、Jetty6チャットルームデモのコードは、イベントチェックのためのAJAXポーリングを処理するコードです。
private void doGetEvents(HttpServletRequest request, AjaxResponse response)
{
member = (Member)chatroom.get(request.getSession(true).getId());
// Get an existing Continuation or create a new one if there are no events.
boolean create=!member.hasEvents();
Continuation continuation=ContinuationSupport.getContinuation(request,create);
if (continuation!=null)
{
if(continuation.isNew())
// register it with the chatroom to receive async events.
member.setContinuation(continuation);
// Get the continuation object. The request may be suspended here.
Object event= continuation.getEvent(timeoutMS);
}
// send any events that have arrived
member.sendEvents(response);
// Signal for a new poll
response.objectResponse("poll", "");
}
別のユーザがチャットルームで発言すると、別スレッドでメソッドが呼び出されてイベントが各メンバへ通知されます。
class Member
{
public synchronized void addEvent(Event event)
{
_events.add(event);
if (_continuation!=null)
// resume requests suspened in getEvents
_continuation.resume(event);
}
...
}
舞台裏の仕組みはこうです。Javaにはスレッドを一時停止して後になってそのスレッドを再開するメカニズムが全くありません。そのためJettyは、JavaとServlet仕様で動作するように、ちょっとズルをしています。まず最初に、リクエストハンドラがcontinuation.getEvent(timeoutMS)を実行すると、RetryReqeuest実行時例外が投げられます。この例外は、すべてのリクエストハンドラのコードを通り抜け、Jettyによってキャッチされ特別に処理されます。そしてエラーを返す代わりに、Jettyは、タイムアウトキューにリクエストを置き、スレッドプールにスレッドを返すのです。
タイムアウトになるか、別のスレッドがcontinuation.resume(event)を呼ぶと、リクエストが再試行されます。この時、つまりcontinuation.getEvent(timeoutMS)が呼ばれる時、タイムアウトを示すnullか、イベントが返されます。そして、リクエストハンドラは通常どおり、レスポンスを生成します。
つまり、このメカニズムは、HTTPリクエスト処理のステートレスな性質を使って、一時停止やや再開をシミュレートしているのです。実行時例外のおかげでスレッドは、リクエストハンドラも、どんな上流フィルタ/サーブレットも、どんな関連するセキュリティコンテキストも、正常に抜け出すことができます。そしてリクエストが再開されると、先程と反対に、フィルタ/サーブレットチェーンと、全てのセキュリティコンテキストを再度通り抜け、continuationの直前のポイントで通常の処理を続行させます。
最後にもう一つ、ContinuationsのAPIは色々なサーバーで動作します。ContinuationsをJetty6以外のサーバで実行する場合は、getEventの中でリクエストをブロックする形でwait/notifyを使うだけで良いのです。狙い通りContinuationsが上手く動くことがはっきりすれば、Servlet 3.0のJSRの一部としてこれらを提案しようと思っています。