SeamFramework.orgCommunity Documentation

第23章 キャッシュ

23.1. Seamでキャッシュを利用する
23.2. ページ断片のキャッシュ

In almost all enterprise applications, the database is the primary bottleneck, and the least scalable tier of the runtime environment. People from a PHP/Ruby environment will try to tell you that so-called "shared nothing" architectures scale well. While that may be literally true, I don't know of many interesting multi-user applications which can be implemented with no sharing of resources between different nodes of the cluster. What these silly people are really thinking of is a "share nothing except for the database" architecture. Of course, sharing the database is the primary problem with scaling a multi-user application — so the claim that this architecture is highly scalable is absurd, and tells you a lot about the kind of applications that these folks spend most of their time working on.

データベースの共有をなるべく少なくできる方法があれば、すべて実行する価値があります。

そこでキャッシュの登場です。しかも1種類ではありません。 Seamアプリケーションを正しく設計すれば、何層にもわたる豊富なキャッシング戦略をアプリケーションのすべての層で利用することができるのです。

2次キャッシュは非常に複雑な概念ですので、詳細についてはお使いのORMソリューションの文書を参照してください。この章では、cacheProviderコンポーネントや<s:cache>によるページ断片のキャッシュなどを直接利用する方法について説明します。

組み込みのcacheProviderコンポーネントは以下のインスタンスを管理します。

不変のJavaオブジェクトであれば安全にキャッシュに置くことができ、オブジェクトはクラスタ内でレプリケーションされます(レプリケーションが有効な場合)。変更の可能性があるオブジェクトをキャッシュに持ちたい場合は、使用するキャッシュ実装の関連文書を読み、キャッシュの変更をキャッシュに知らせる方法を調べてください。

cacheProviderを使うには、使用するキャッシュ実装の以下のjarファイルをプロジェクトに含めてください。

SeamにEARをデプロイする場合は、キャッシュのjarファイルと設定ファイルをEARに直接含めることをお勧めします。

JBossCacheを使う場合はさらに設定ファイルが必要です。treecache.xmlに適切なキャッシュ設定を記述し、クラスパスに含めます(たとえばEJB JARファイルやWEB-INF/classesなど)。JBossCacheには恐ろしく厄介で紛らわしい設定がたくさんあるので、ここでは説明しません。詳細はJBossCacheの文書を参照してください。

treecache.xmlのサンプルはexamples/blog/resources/treecache.xmlにあります。

EHCacheは設定ファイルがなくてもデフォルトの設定で動作します。

設定ファイルを変更するには、components.xmlのキャッシュ設定を変更してください。


<components xmlns="http://jboss.com/products/seam/components"
            xmlns:cache="http://jboss.com/products/seam/cache">
   <cache:jboss-cache-provider configuration="META-INF/cache/treecache.xml" />
</components
>

Seamコンポーネントにキャッシュをインジェクトするには以下のように記述します。

@Name("chatroomUsers")

@Scope(ScopeType.STATELESS)
public class ChatroomUsers
{
    @In CacheProvider cacheProvider;
    @Unwrap
    public Set<String> getUsers() throws CacheException   {
        Set<String> userList = (Set<String>) cacheProvider.get("chatroom", "userList");
        if (userList==null) {
            userList = new HashSet<String>();
            cacheProvider.put("chatroom", "userList", userList);
        }
        return userList;
    }
}

キャッシュを複数設定する場合は、components.xmlを使用してください。


<components xmlns="http://jboss.com/products/seam/components"
            xmlns:cache="http://jboss.com/products/seam/cache">
   <cache:jboss-cache-provider name="myCache" configuration="myown/cache.xml"/>
   <cache:jboss-cache-provider name="myOtherCache" configuration="myother/cache.xml"/>
</components
>

Seamのキャッシュの利用でもっとも興味深いのは、JSFにおけるページ断片のキャッシュ問題を解決する<s:cache>タグです。<s:cache>は内部的にpojoCacheを使うので、使用する場合は前述の手順を行ってください(EARにjarファイルを含める、やっかいな設定を切り抜ける、などです)。

<s:cache>は、あまり変更されないレンタリングコンテンツに使用してください。 たとえば、最新のblogエントリを表示するblogのウェルカムページです。


<s:cache key="recentEntries-#{blog.id}" region="welcomePageFragments">
   <h:dataTable value="#{blog.recentEntries}" var="blogEntry">
      <h:column>
         <h3
>#{blogEntry.title}</h3>
         <div>
            <s:formattedText value="#{blogEntry.body}"/>
         </div>
      </h:column>
   </h:dataTable>
</s:cache
>

keyを指定することによって、各ページ断片のキャッシュバージョンを複数持つことができます。この例では、一つのblogに対して一つのキャッシュバージョンが存在します。regionには、すべてのバージョンを保存するキャッシュまたはリージョンのノードを指定します。異なるノードは異なる有効期限ポリシーを持つ場合があります。(前述の厄介な設定で指定できます。)

そして、 <s:cache> の大きな問題は、 対象のデータがいつ変更されるか (たとえば、 新しい blog がいつ投稿されるか) を知り得ないということです。 つまり、 キャッシュされた断片は、 明示的にキャッシュから排除する必要があります。

public void post() {

    ...
    entityManager.persist(blogEntry);
    cacheProvider.remove("welcomePageFragments", "recentEntries-" + blog.getId() );
}

あるいは、変更を即座にユーザーに見せる必要がないのであれば、JBossCacheノードの有効期限を短く設定しても良いでしょう。