2009년 10월 31일 토요일

OSCashe (フィルタキャッシュ、フラグメントキャッシュ、データキャッシュ)

http://www.opensymphony.com/oscache/

1. フィルターキャッシング

 フィルターキャッシングは、動的コンテンツと静的コンテンツ(PDFや画像ファイルなど)をキャッシングするのに使われます。

  動的コンテンツでは、特定のJSPやサーブレットの結果をまるごとキャッシングしたい場合などに使用されます。静的コンテンツでは、ファイルをメモリに キャッシングさせることにより、HDDへのアクセスをなくし、高速にファイルをロードすることができます。これはApacheなどにもある機能で、大きい ファイルがある場合や、アクセス数が非常に多い場合に有効です。

 サンプルでは、静的コンテンツのキャッシングを使用しています。全てのgifファイルをメモリにキャッシュさせる設定を行い、「profile.gif」という画像をキャッシングしています。

 以下にソースと実装方法を記述します。

「web.xml」の一部
<filter>
    <filter-name>CacheFilter</filter-name>
    <filter-class>
        com.opensymphony.oscache.web.filter.CacheFilter
    </filter-class>
    <init-param>
        <param-name>time</param-name>
        <param-value>3600</param-value>
    </init-param>
    <init-param>
        <param-name>scope</param-name>
        <param-value>application</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.gif</url-pattern>
</filter-mapping>

 上記の設定により、1時間(3600秒)の間、applicationのスコープで全てのgifファイルをキャッシングすることができます。この方法ではcom.opensymphony.oscache.web.filter.CacheFilterというフィルタークラスを使用します。

 また、ここでは静的コンテンツの例を取り上げましたが、動的コンテンツをキャッシングする場合には、url-patternに特定のJSPやサーブレットのURLを入力すればOKです。

  ただしその場合、パフォーマンス改善としては非常に有効ですが、そのコンテンツの項目全てがキャッシングされてしまうため、一部の情報は最新のものを表示 したい場合などには向きません。このサンプルでは、全体をキャッシングするページはないため使用していませんが、画面の仕様によっては非常に有効な使い方 となります。

 サンプルアプリケーションを動作させる方は、下の値をカスタマイズしてキャッシングの動作を確認してみて下さい。

上記「web.xml」のカスタマイズ項目
timeキャッシュされる期間を秒で設定
scopeキャッシュが有効なscopeをsession/applicationの何れかで設定
url-patternキャッシュ対象のコンテンツのURLパターンを設定

2. フラグメントキャッシュ

 OSCacheのカスタムタグを使用すれば、JSPの特定の部分をキャッシングすることができます。サンプルでは、「現在の日時」をJSP内のコードで生成しており、これを毎回生成しないようにキャッシングする実装をしています。

 以下にソースと実装方法を記述します。「web.xml」に、OSCacheのカスタムタグを使用できるように設定します。

「web.xml」の一部
<jsp-config>
    <taglib>
        <taglib-uri>http://www.opensymphony.com/oscache</taglib-uri>
        <taglib-location>/WEB-INF/oscache.tld</taglib-location>
    </taglib>
</jsp-config>

 次に、キャッシングしたいJSPの箇所をOSCacheのタグで囲みます。

例文
<cache:cache time="60">
    ・・・ jsp content ****
</cache:cache>

 ここでは、60秒(1分)間キャッシングするという設定をしています。

 しかし、「新しく"分"の単位が切り替わる度にキャッシュを破棄したい」という様な場合には不向きです。この場合にはtime属性ではなく、cron属性にcron式を設定します。これはUNIXのスケジューラとして利用されている「cron」の設定方法と同じ様に、キャッシュの期間を設定することができる大変強力で便利なものです。詳細は、OpenSymphonyのサイトを参照して下さい。

 上記の例文をcron式で記述したのが下記になります。

「profile.jsp」のソース一部
<%@taglib prefix="cache" uri="/WEB-INF/oscache.tld" %>
・・・・・・・・・・
    <cache:cache cron="* * * * *">
        現在の日時:<%= new java.text.SimpleDateFormat(
"yyyy/MM/dd hh:mm").format(new java.util.Date()) %><br>
    </cache:cache>

 サンプルアプリケーションを動作させる方は、下の値をカスタマイズしてキャッシングの動作を確認してみて下さい。

上記cacheタグの属性のカスタマイズ項目
timeキャッシュされる期間を秒で設定します
cronキャッシュされる期間をcron式で設定します

3.データキャッシュ

 DBから取得した情報などをキャッシングするには、JSPではなくサーブレットで処理する必要があります。com.opensymphony.oscache.base.Cacheクラスを利用して実装することが可能です。サンプルでは、Profileというデータクラスを生成しそのインスタンスをキャッシングしています。

 以下にソースと実装方法を記述します。

「OSCacheSampleServlet.java」のソース一部
public static final String
    PROFILE_CACHE_KEY = "profile_cache_key";
public static final String
    CACHE_OBJECT_KEY = "__oscache_cache"; //cache.propertiesに定義

/**
 * @param req
 * @param res
 * @throws ServletException
 * @throws IOException
 */
public void service(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException{
    
    Cache cache = (Cache)getServletContext()
                      .getAttribute(CACHE_OBJECT_KEY);

    Profile profile = null;
    try{ //キャッシュからプロフィールを取得
        profile = (Profile)cache.getFromCache(PROFILE_CACHE_KEY,30);
    }catch(NeedsRefreshException nre){
        //キャッシュに存在しない or キャッシュの期限切れ
        profile = new Profile();
        profile.setName       ("山田 太郎"             );
        profile.setAge          ("18"                     );
        profile.setComment  ("はじめまして"           );
        profile.setCacheDate(new SimpleDateFormat(
            "yyyy/MM/dd hh:mm:ss").format(new Date()));
        //キャッシュに登録
        cache.putInCache    (PROFILE_CACHE_KEY,profile);
    }

    req.setAttribute("profile",profile);
req.getRequestDispatcher( "profile.jsp" ).forward(req,res);

}

 CacheクラスはOSCacheのキャッシュシステムそのものを表現したクラスです。この クラスを利用して、キャッシュシステムにオブジェクトを格納したり、取り出したりすることができます。このクラスのインスタンスはサーブレットコンテキス トに格納され、そのキー名は「oscached.properties」に記述されているcache.key(デフォルトでは"__oscache_cache")となります。

 キャッシュに格納されたオブジェクトを取り出すには、

getFromCache(key名,キャッシュの期間(/秒))

 逆に格納するには

putInCache(key名,キャッシュ対象Object)

 を使用します。上記処理では、30秒でキャッシュが破棄されることになります。

 getFromCache()を実行する場合は、NeedsRefreshExceptionがthrowされる可能性があります。これはcacheが存在しない場合やキャッシュの有効期限切れの場合にthrowされる例外ですので、必ずそれを想定して例外処理を実装して下さい。

 サンプルではその例外をcatchすると、新しいオブジェクトを生成しています。ここは通常のシステムであればDBからの取得処理になることが多いはずです。

 サンプルアプリケーションを動作させる方は、下の値をカスタマイズしてキャッシングの動作を確認してみて下さい。

上記cacheタグの属性のカスタマイズ項目
getFromCachの第二引数キャッシュされる期間を秒で設定します

oscached.propertiesの設定方法

 ここまでは、特に「oscache.properties」の設定について触れていませんでした。サンプルやテストで動作させる程度であれば特に初期設定でも問題有りませんが、状況に応じて独自の設定をすることが望ましいのは言うまでもありません。

 ここでは、主なプロパティを一覧で紹介したいと思います。

「oscached.properites」の主なプロパティ
プロパティ名説明
cache.memoryメモリにキャッシュするか?(falseならHDにキャッシュ)
cache.keyサーブレットコンテキストに格納されているCacheオブジェクトのキー名
cache.capacityキャッシュのサイズ
cache.pathキャッシュが保存されるディレクトリパス
cache.unlimited.diskディスクへのキャッシングを無制限に行うか?(falseなら行わない)

まとめ

 今回はOSCacheを用いたWEBコンテンツのキャッシングについて紹介しました。しかし、「キャッシングの有効性は理解したが、実務のどの場面で使用したらいいか分からない。」といった方も多いのではないでしょうか?

  まず、Webのパフォーマンスチューニング全般に言えることですが、最初にしなければいけないことは、どのページを表示する時/どの処理を行う時に遅い と、ユーザが不満を持っているかを調べることです。これを行わずに、闇雲にキャッシング処理を入れたところで自己満足で終わってしまいます。

 そして、その処理やページの一部分をキャッシングしても問題ないかを考えてみて下さい。

  例えば、非常に複雑なSQLで統計情報を取得し、CVSでダウンロードするような処理があったとします。これを、「その時点での最新の統計」ではなく、 「10分ごとに最新になる統計」という仕様にして、「データキャッシュ」または「フィルターキャッシュ」を利用し、10分間キャッシングすることができれ ば、WEBサーバーとデータベースサーバーの負荷が軽減されるはずです。

 他にも、非常にアクセスが多い人気サイトなどの場合、日記の様な コンテンツの更新をすると、「更新を受け付けました。更新が反映されるまでには数分程度かかります。」などと表示して、キャッシュを使っている例も見受け られます。こういったものも、「データキャッシュ」を使えば実装することが可能です。

 以上、長くなりましたが、OSCacheはOpenSymphonyのサイト(英語)以外ではあまりドキュメントが存在しないため、この記事がこれから使ってみようという方のお役に立てば幸いです。

参考資料

  1. opensymphony oscache

댓글 없음:

댓글 쓰기