Springのための宣言的キャッシングサービス

Alex Ruiz著
2006年5月19日

概要

スケーラビリティ、信頼性、高いパフォーマンスは、最新のJ2EEアプリケーションにおいては不可欠な要件です。クライアントの種類に関係なく、通 常、リクエストの処理には、控えめに言っても、異種データソースからの情報収集や複雑な計算の実行など、パフォーマンスに対して悪影響を与えるアクション が含まれています。キャッシングは、エンタープライズアプリケーションのパフォーマンスを向上させる必須プラクティスの1つです。どのアプリケーションに も、アプリケーション固有のキャッシング要件があります。この要件は、パフォーマンスの低下を発生させないように、常に調整しておかなければなりません。 エンタープライズアプリケーションには、アプリケーションのコードに触れることなく、キャッシング機能を追加したり調整するための簡単な方法が必要です。 この記事では、Springベースのアプリケーションのためのコードフリーのキャッシングフレームワークである、 Springのための宣言的キャッシングサービスを紹介します。

キャッシングの概要

簡単に言うと、キャッシングには、取得操作が高くつくようなデータの一時的な保管が含まれます。その結果、元々のデータソースからの余分なデータ取 得がなくなり、クライアントへの素早い応答が可能になります。たとえば、アプリケーションサーバとデータベースが同じ場所に配置されておらず、ネットワー ク上の通信が伴う場合、キャッシングによって、アプリケーションサーバからデータベースへの呼び出しを回避すれば、絶大なパフォーマンス向上が可能です。 キャッシングによって、データベースの作業負荷が削減でき、ネットワークの帯域幅に制限されないので、アプリケーションの応答時間は著しく改善されます。

分散システムは、キャッシングサービスのための絶好のターゲットです。リモート呼び出しは、特に速度が遅く、貴重なネットワーク帯域幅を消費するの で、パフォーマンスの問題が発生します。リモートサービスへの呼び出しは、パラメータを、ネットワークを介して転送可能な形式(XML、バイトストリーム など)にマーシャリングしてリクエストを作成することから始まります。一旦、リクエストが受信されると、サーバー側では、処理を開始するために、マーシャ リングを解除する必要があります。サービスによって生成された応答にも、同様の手続きが適用されます。これによって、さらにオーバヘッドが加わります。 キャッシングを利用すると、リモート呼び出しがなくなるか、少なくとも削減できるので、分散アプリケーションのパフォーマンスが劇的に向上します。

このような利点がある一方、キャッシングによって、エンタープライズアプリケーションの設計、開発、デプロイがかなり複雑になります。ビジネス要件 についての明確な知識なしに、キャッシングを適用するべきではありません。また、キャッシングを適用するかどうかの判断は、テスト結果、その他の確固たる 証拠に裏づけされていなければなりません。

キャッシングのための基本的なガイドラインを以下に示します。

  • リモート呼び出しを削減するためにキャッシングを追加することは、通常は価値があります。ネットワーク上を往復する必要がなくなり、パフォーマンスが向上します。
  • 読み取り専用の参照データのキャッシングは、最適化に不可欠です。たとえば、米国の州のリストをキャッシングすれば、ほとんど変更されることのないデータの継続的な取得をなくすことができます。
  • 与えられたパラメータ値セットに対して、常に同じ情報を返すサービス応答は、キャッシングに最適です。
  • 動 的データ(読み取り/書き込みデータ)のキャッシングは、アプリケーションが古くなったデータを許容する場合は、適用できます。ニュースWebサイトが良 い例です。このようなサイトでは、キャッシングによるパフォーマンス向上のためなら、数秒遅れのニュースが表示されても許されます。
  • キャッシュされたデータの量を制御できなければなりません。さもないと、メモリを使い過ぎて行き詰ってしまいます。
  • クラスタを越えての動的データのキャッシングと、各ノード内のデータの同期は、困難です。サードパーティ製のキャッシングソリューションの使用を検討することをお勧めします。
  • リアルタイムデータ(株式相場など)や 機密データ(パスワード、社会保障番号など)のキャッシングは避けるべきです。
  • キャッシングによって、セキュリティおよび監査に関連する問題が発生する場合があります。キャッシュは、リクエストをインターセプトして、セキュリティチェックなしに処理するので、セキュリティスクリーニングを破ってしまう可能性があります。

独自のキャッシュを実装するのは、簡単な作業ではありません。スレッド管理、古くなったデータの削除、クラスタリングサポートなどの複雑な問題を処 理しなければなりません。最近では、オープンソースと商用の両方で、高品質なサードパーティ製のキャッシュ実装が数多く提供されています。これらを利用す ると、キャッシュを作成したり、独自のキャッシュを保守するというもっと重要な作業の負担を軽減できます。

Springのための宣言的キャッシングサービス

Springフレームワークは オープンソースのアプリケーションフレームワークで、プレーンなJavaオブジェクトにエンタープライズサービスやその他の機能を提供します。 Springを利用して作成されるアプリケーションのほとんどが、データ中心型の多層エンタープライズアプリケーションで、複数の同時実行ユーザを持ちま す。このようなアプリケーションも、リモートEJB、Webサービス、CORBAなどのリモート技術を使用して、外部システムを統合する場合があります。 エンタープライズアプリケーションは、基幹業務にとって重要なので、高いパフォーマンスが期待されます。

Springのための宣言的キャッシングサービスは、Springを利用したアプリケーションのための宣言的キャッシングを提供 します。宣言的キャッシングには、プログラミングが一切含まれません。このため、キャッシングサービスの適用や調整を、かなり簡単に素早く行うことができ ます。キャッシングサービスの設定は、Spring IoCコンテナの中ですべて実行することができます。宣言的キャッシングには以下の特徴があります。

  • EHCacheJBoss CacheJava Caching System (JCS)OSCacheTangosol Coherenceなどの様々なキャッシュプロバイダをサポートします。
  • キャッシングサービスのプログラムでの使用のために、前述のAPIよりも統一された、単純で使いやすいAPIを提供しています。キャッシングのプログラムでの使用は、特別な要件がない限り、お勧めできません。
  • 宣言的キャッシングサービスは、古くなったデータの保管を防止するための、宣言的キャッシュフラッシングも提供しています。
  • 様々な宣言的コンフィグレーション方法をサポートします。
  • 宣言的キャッシングサービスは、他のキャッシュプロバイダをサポートするように簡単に拡張できます。

宣言的キャッシングサービスの利点

宣言的キャッシングとその利点を理解するために、以下のコード部分を見ていきましょう。これは、顧客IDが与えられると、データソースから顧客情報を取得するという顧客マネージャの簡単な実装です。パフォーマンスを向上させるために、データアクセスレイヤから取得した Customerオブジェクトは、プログラムでCoherenceキャッシュに保存されます。さらに、古くなったデータの保管を防ぐために、1人の顧客の情報が更新されると、顧客情報を含むキャッシュはフラッシュされます。

public class CustomerManager {

  private CustomerDao customerDao;

  private CacheKeyGenerator keyGenerator;    

  

  public Customer load(long customerId) {

     
                          
validateCustomerId(customerId);

    Customer customer = null;

    

    Serializable key = keyGenerator.generateKey(customerId);

    NamedCache cache = getCache();

    customer = (Customer) cache.get(key);

    if (customer == null) {

       
                          
customer = customerDao.load(customerId);

      cache.put(key, customer);

    }

     
                          
return customer;

  }



  public void update(Customer customer) {

     
                          
customerDao.update(customer);  

    Serializable key = keyGenerator.generateKey(customer.getId());

    NamedCache cache = getCache();

    cache.remove(key);

  }       

  

  private NamedCache getCache() {

    return CacheFactory.getCache("customerCache");

  }

  // rest of class implementation

}
                        

キャッシュの使用によって、アプリケーションのパフォーマンスは向上しますが、余分な(不必要な)複雑さが加わります。キャッシュのプログラムでの使用によって、以下の問題が発生します。

  • コードが理解しにくくなる。コア機能を把握するのがかなり難しくなります。この例では、メソッドの実際の仕事を実装している部分は、太字の行だけです。
  • コードの保守が難しくなる。 キャッシュプロバイダの呼び出しが埋もれてしまったり、アプリケーションを越えて分散してしまいます。その結果、キャッシングを含む保守作業は、システム のコア機能に影響を与えやすくなります。さらに、キャッシング機能の変更には、コード部分のすべてのコピーを調査する作業が含まれます。さらに悪いこと に、その際に、それらがどこにあるのかがわかりません。
  • コードのテストが難しくなる。この例では、メソッドのビジネスロジックをテストするために、Coherence CacheFactory、または、少なくともそれをシミュレートするオブジェクトが必要です。
  • コードの再利用が難しくなる。この CustomerManagerクラスを、異なるキャッシングニーズを持つアプリケーションや、キャッシングの必要がないアプリケーションで再利用するのは困難(または不可能)です。

宣言的キャッシングは、キャッシングが実行される仕組みをカプセル化し、キャッシュ実装への依存性をJavaコードから除去します。宣言的キャッシングを導入すると、前の例のメソッドは、以下のようにコア要件だけを実装したものになります。

public class CustomerManager {

  private CustomerDao customerDao;

  

  public Customer load(long customerId) {

    validateCustomerId(customerId);

    return customerDao.load(customerId);

  }

  

  public void update(Customer customer) {

    customerDao.update(customer);    

  }  

  // rest of class implementation

}

以下のXML部分は、Javaコードではなく、Spring IoCコンテナで、 CustomerManagerインスタンス用にキャッシングサービスをどのように設定するかを示しています。

<bean id="
                          
customerDaoTarget"

  class="org.springmodules.cache.samples.dao.CustomerDao" />

  <!-- Properties -->

</bean>



<coherence:proxy id="customerDao" refId="
                          
customerDaoTarget">

  <coherence:caching methodName="load" cacheName="customerCache" />

  <coherence:flushing methodName="update" cacheNames="customerCache" />

</coherence:proxy>



<bean id="customerManager"

  class="org.springmodules.cache.samples.business.CustomerManager" />

  <property name="customerDao" ref="customerDao" />

</bean>
                        

宣言的キャッシングのコンフィグレーションは、キャッシング機能を、アプリケーションのコア要件から効果的に分離します。そして、前述の問題を解決し、以下のような利点を提供します。

  • 責務のより明確な分離。システム内のモジュールは、コア要件に対する責務のみを負い、キャッシング機能に対する責務は持ちません。その結果、追跡性が向上します。
  • モジュール性が高まる。キャッシュ機能をコアモジュールから分離することで、重複したコードが大幅に削減されます( once and only onceの原則をサポート)。また、コードの分散を防ぐのにも役立ちます。
  • 設計上の決定のレイトバインディング。宣言的キャッシングを利用すると、開発者は、キャッシュの実装や調整についての決定を遅らせることができます。その代わり、開発者は、アプリケーションの最新のコア要件に集中できます。宣言的コンフィグレーションは、 YAGNI(you aren't gonna need it)をサポートします。開発者は、キャッシングが本当に必要になった時にのみ、システム全体を変更せずに、アプリケーションにキャッシングを追加できます。

Pages: 1, 2, 3, 4

Next Page »