サーブレットの最適化

このセクションでは、Web アプリケーションのサーブレットコンポーネントのコードを改善するための具体的な手法について説明します。Web アプリケーションの要素を変更する予定がある場合は、デザインパターンの使用も検討する必要があります。デザインパターンの詳細については、弟 2 章、「デザインパターン」を参照してください。

init メソッドでのスタティックデータのキャッシュ

可能な場合は、クライアントがリクエストするたびに Web アプリケーションがスタティックデータをダイナミックに生成するのではなく、キャッシュするようにしてください。サーブレットを使用して、init メソッドでスタティックデータをキャッシュすることができます。

サーブレットが web.xml ファイルに登録されており、load-on-startup 要素が 1 に設定されていると、JRun はサーバの起動時に init メソッドを呼び出します。そうでない場合は、クライアントが最初にサーブレットをリクエストしたときに (最初のみ)、JRun は init メソッドを呼び出します。

次の例はサーブレットの init メソッドを示しています。このメソッドは、アプリケーションの web.xml ファイルから初期化パラメータを取得し、サーブレットの doGet メソッド内で後で使用されるフッターを作成します。

ServletConfig オブジェクトを介して init メソッドを呼び出すことも、介さずに呼び出すこともできます。ただし、この場合は、初期化パラメータにアクセスしてフッターを作成できるように、ServletConfig を使用してメソッドを呼び出します。

public class InitOverride extends HttpServlet {
  String name;
  String email;
  String copyright;
  String footer;
  public void init (ServletConfig config) throws ServletException {
    super.init(config);
    name = config.getInitParameter("name");
    email = config.getInitParameter("email");
    copyright = config.getInitParameter("copyright");
   footer = ("<HR><FONT FACE=¥"arial,helvetica,sans-serif¥" 
SIZE=-2>Copyright " + copyright + ".Brought to you by:<A 
HREF=¥"mailto:"+ email + "¥">" + name + "</A></FONT>");
  }
  public void doGet ( HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    response.setContentType("text/html");
...
    out.println(footer);
    out.println( "</body></html>" );
  }
}

次の行は、初期化パラメータを含めた web.xml ファイル内のサーブレット定義を示しています。

<servlet>
 <servlet-name>InitOverride</servlet-name>
 <servlet-class>InitOverride</servlet-class>
 <init-param>
  <param-name>name</param-name>
  <param-value>Nick Danger</param-value>
 </init-param>
 <init-param>
  <param-name>email</param-name>
  <param-value>ndanger@sandstonebuilding.com</param-value>
 </init-param>
 <init-param>
  <param-name>copyright</param-name>
  <param-value>2000, 2001, 2002</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
</servlet>

ServletContext オブジェクトでのスタティックデータのキャッシュ

ServletContext オブジェクトはオブジェクトを属性として保管します。アプリケーション内のすべてのサーブレットが同じコンテキストにアクセスするため、ハッシュテーブルなどの構造のようなオブジェクトをキャッシュすることによって、アプリケーションの処理負荷を軽減できます。さらに、オブジェクトをサーブレットの init メソッド内のコンテキストに保管すると、メソッドは 1 回しか実行されません。

サーブレットの init メソッドを使用してオブジェクトを作成し、ServletContext 内に保管し、ServletContext.getAttribute への呼び出しを使用してサーブレットの本文内のオブジェクトにアクセスします。データベーステーブルの場合は、ResultSet オブジェクトを属性として保管できません。配列などのオブジェクトとしてデータの要素を変更する必要があります。

次の例では、サーブレットの init メソッド内で ResultSet オブジェクトを反復処理し、コンテキストの setAttribute メソッドを使用して ServletContext 内に Hashtable オブジェクトを保管します。サーブレットは doGet メソッド内で ServletContext を介して Hashtable オブジェクトにアクセスし、リクエストパラメータを使用して適切な出力行を取得します。

public void init () throws ServletException {
...
ResultSet rs = stmt.executeQuery(sqlstmt);
Hashtable h = new Hashtable();
while (rs.next()) {
  h.put(rs.getString("ABBR"),rs.getString("LONGNAME"));
  }
getServletContext().setAttribute("statelist", h);
...
}
public void doGet (HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException {
  ...
  Hashtable h = (Hashtable) getServletContext(). 
getAttribute("statelist");
  String state = (String) h.get(request.getParameter("state"));
  out.println(request.getParameter("state") + " is an abbreviation for 
" + state);
  ...
}

JSP で作業を行う場合は、ServletContext オブジェクトとして暗黙に宣言されている application オブジェクトを使用します。

println の代わりに print メソッドを使用

PrintWriter オブジェクトにデータを送信する際は、次の例のように println メソッドではなく print メソッドを使用します。

PrintWriter out = response.getWriter();
//out.println("これは効率のよくない出力です。");
out.print("これは効率のよい出力です。");

Java では、print メソッドの方が println メソッドよりも効率のよい出力メソッドです。println は内部で入力を取り込み、print に送信します。printlnprint の使用方法の違いは、println が文字列の最後に改行文字を挿入することです。しかし、クライアントで HTML のソースを表示しないかぎり、出力のレンダリング方法は変わりません。

HttpSession オブジェクトによるステート管理

ステート管理の場合、非表示フィールド、Cookie、および URL リライティングではなく HttpSession オブジェクトを使用します。HttpSession API を使用する際に、JRun はクライアントとサーバ間でセッションデータ自体ではなくセッション ID だけを渡します。これは、セッションオブジェクトの API により実装の詳細が非表示になっているため、他のメソッドよりも使用し易くなっています。

JSP 開発者の場合は、暗黙のセッションオブジェクトを使用します。

HttpSession オブジェクトを使用したステート管理の詳細については、 「セッションの操作」 を参照してください。

出力のフラッシュ

ページ全体のロードが終了する前にクライアントにページの一部が表示されるように、定期的にデータをフラッシュします。それによってアプリケーションの全体的なレスポンス速度が速くなるわけではありませんが、ユーザーはページがより速やかに処理されたように感じます。

この手法は、ダウンロードに時間がかかるグラフィックスが多く含まれているセクションや多くの処理が必要なセクションがある場合に有効です。

次に例を示します。

PrintWriter out = response.getWriter();
out.print(header);
out.flush(); //ヘッダーをフラッシュします。
...
out.print("/images/large_graphic.gif");
out.flush(); //本文内の容量の大きいグラフィックをフラッシュします。
...
out.print(footer);
out.flush(); //フッターをフラッシュします。

この手法は、暗黙の out オブジェクトとともに JSP ページに使用します。

レスポンスオブジェクトのバッファーサイズの拡張

サーブレットはレスポンスオブジェクトのバッファー内のコンテンツをロードします。バッファーが満杯の場合、サーブレットはクライアントへのソケット接続を作成し、バッファーをフラッシュします。ソケットおよびネットワークトラフィックの数を減らすには、バッファーの容量を大きくします。

サーブレットのレスポンスバッファーのデフォルト値は 4 KB です。レスポンスサイズがこのサイズに到達すると、JRun はバッファーをフラッシュします。次の例のように setBufferSize メソッドを使用して、バッファーに大きい値を設定し、必要なフラッシュ回数を減らします。

response.setBufferSize(8192);

バッファーのサイズを確認するには、次の例のように getBufferSize メソッドを使用します。

out.print("バッファーサイズは " + response.getBufferSize() です。);

PrintWriter オブジェクトのバッファーサイズの拡張

PrintWriter のバッファーサイズを拡張すると、JRun によるレスポンスデータのフラッシュ回数が減少します。その結果、オープンソケットや接続の数が減少します。

次の例のように ByteArrayOutputStream オブジェクトを使用して PrintWriter のバッファーサイズを大きくします。

...
response.setContentType("text/html");
ByteArrayOutputStream bos = new ByteArrayOutputStream(8192);
PrintWriter out = new PrintWriter(bos, true);
out.print("<html><head><title>Using BytaArrayOutputStream</title></
head>");
out.print("<body>");
...
out.print( "</body></html>" );
response.setContentLength(bos.size());
bos.writeTo(response.getOutputStream());
...

ServletContext.log への呼び出し制限

ServletContext.log メソッドへの呼び出しにより、パフォーマンスが低下する場合があります。可能な範囲内でこのメソッドへの呼び出しを制限し、System.out.println への呼び出しを使用します。