Hatena::ブログ(Diary)

oct inaodu

 | 

2005-09-20

AJAXアプリケーションのデバッグ、JavaScriptログ出力、Log4js


f:id:brazil:20050920193147g:image


Ajax非同期通信アプリケーションデバッグを行う際に、以下ライブラリを利用し、ログ出力を行ないながら動作詳細を確認した。フランス製。



前提

  • クライアントサーバーの通信をリアルタイムでログ出力を確認したい。
  • Venkmanでステップ実行を行うと、タイムアウトが発生する。また正確なタイミングで振る舞いを確認できない。
  • debug.jsは、まとめてフラッシュするため利用は難しい。

特徴

  • ログを出力すると、リアルタイムに行が増えていく。
  • ソートや、フィルタも存在する。
  • debugや、infoなどのカテゴリ分けがある。
  • ログと一緒に、オブジェクトインスペクタも表示できる。

利用手順

  <!-- 相対パスの指定をすること -->
  <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>
  • ログ出力したい部分に、以下を記述。debug、info、error、warnの4つのメソッドがある。フランス語、Niveau(レベル)、Fichier(ファイル)、Fonction(ファンクション)。
  /*
   * ログを出力する。
   * @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」から、実行を確認できる。

RIAのRIA開発環境


f:id:brazil:20050920165858j:image



ブラウザ上で動く総合開発環境。コーディング、ワークフローエディタ、問題管理、ER図作成、コミュニケーション、本当に大体揃っている。

動きもデザインも不味いが、瑣末なことだ。


現在、RSSリーダーメーラーIME、ゲーム、音楽/映像再生、ワープロ表計算、総合開発環境と、日常使うものは全てブラウザに乗っている。

OSや開発環境を、CDで持ち歩くようなナイーブな状況は考えられない。


これからはインターネットの時代だなー、と思う。

私にとってこれは、今使っているThinkpadが、最後に買ったパソコンになるということだ。


実行手順

  1. JDKをセットアップしておく。
  2. フルセットをダウンロードし、スペースを含まないディレクトリへ展開する。
  3. MySQLを実行する(C:\IABStudio\startmysql.bat)。
  4. JBossを実行する(C:\IABStudio\startjboss.bat)。
  5. 起動を待ち、http://localhost:19080/WAB/loginアクセスする。
  6. admin:adminでログインする。

Jetty 6 Continuations - Ajax対応!

(via こんな毎日・・・)


原文: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サーバが、イベントやタイムアウトが発生するまでポーリングリクエストを保留することがよくあります。つまり、アイドリングしているAJAXアプリケーションサーバーには、非同期イベントが起きたらすぐにレスポンスをクライアントに送ることを目的として、保留されているリクエストがあるということです。これは素晴らしいテクニックですが、「リクエスト単位スレッド」モデルを台無しにしてしまいます。何故なら、そのような状態では、あらゆるクライアントサーバへの未解決のリクエストを持つことになるためです。したがって、再びサーバは各クライアントあたり1つ以上のスレッドが必要となり、再び何千人ものユーザに同時に対応する問題に直面するのです。


Jetty 6 Continuations

解決策は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は、JavaServlet仕様で動作するように、ちょっとズルをしています。まず最初に、リクエストハンドラが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の一部としてこれらを提案しようと思っています。

*1:This thread-per-request model allows much greater scaling of connections (users) at the expense of a reduced maximum requests per second for the server as a whole (in Jetty 6 this expense has been significantly reduced).

 |