Web Api (REST サービス) のサーバー サイド Cache


ASP.NET Web API (ASP.NET MVC 4 Beta) では、Server Side Cache を制御する Actoin Filter は提供されていません。(なお、ASP.NET MVC の OutputCache Filter は使用できません。)

環境 : Visual Studio 2010

REST サービス / Web Api の実践

ここでは、応用的なテーマをとりあげます。基本的な構築手順については、「Getting Started with ASP.NET Web Api」 (ASP.NET の場合)、または「REST サービスの作成」 (WCF の場合) を参照してください。

こんにちは。

もっと、のんびりと記載していこうと思ったのですが、エバンジェリスト 井上 (大輔) から「もっと書け」と言われたので、早速、続きを記載したいと思います。

Web Api (REST サービス) の Custom HTTP Header (HTTP ヘッダー)」では、ヘッダーをカスタマイズすることで、クライアント側 (ブラウザー側) の最適化をおこないましたが、今回は、サーバー側について記載します。REST サービスは、HTTP / HTML をベースとしたキャッシュとの相性も良く、チューニングしやすいため、是非こうしたオプションを有効活用して開発してください。

まず復習として (今更ですが)、サーバー サイド キャッシュには、下記のいくつかの手法があります。

  1. IIS の Kernel Mode Cache
  2. IIS の User Mode Cache
  3. ASP.NET Cache

補足 : Windows Azure を使用している場合は、コンテンツ配信ネットワーク (CDN, Contents Delivery Network) を使うことで、ロケーション最適化と、キャッシュによる最適化が可能です。(クエリー文字列も、キャッシュのキーとして認識させることができます。)

ご存じの方も多いと思いますが、IIS / ASP.NET では、内部でパイプライン処理がおこなわれ、このパイプライン上の個々の処理 (例えば、認証処理、ログ、など) をさまざまな形でカスタマイズできるようになっています。そして、上記 (1 - 3) では、下に行くほど、パイプランの内部に入って処理がおこなわれます。例えば、上記の最初の 2 つは、IIS のパイプライン上で処理されますが (特に 1 番目のカーネル キャッシュは、Http.sys のレベルでキャッシュ判定がおこなわれます)、3 番目 (ASP.NET Cache) は、IIS から呼び出される .NET (ASP.NET) のパイプラインで処理されます。
このため、雑な書き方ですが、上記で、上に行くほど応答性が良く (パフォーマンスが速く)、逆に、下に行くほど、より柔軟で細かなカスタマイズが可能になっています。(また、php など、.NET 以外の開発環境では、上記の 3 番目の方法は使用できないので注意してください。)

特に、REST サービスの場合は、同じコンテンツ (アドレス) ならキャッシュを使えば良いという単純な実装にはならず、現実には、クエリー文字列 (例えば、http://...?id=5) や HTTP Header (例えば、Accept : application/json) などによるキャッシュ制御は少なくとも必要になるため、上記の 2 番目か 3 番目の方法でサーバー側のキャッシュ制御 (Cache Control) をおこなうことになるでしょう。
そこで、以降では、上記の 3 番目の方法を使って、より柔軟にキャッシュを活用するためのさまざまな方法を例示して行きたいと思います。

補足 : ここでは説明しませんが、上記の 1 番目、2 番目のキャッシュ制御は、IIS マネージャー (下記) を使って構成できます。

下記は、IIS マネージャーを使ってユーザー モード キャッシュを構成した場合の Web.config のサンプルです。(開発成果物の配布時は、この構成情報をそのまま配布することで、キャッシュ設定が有効になります。)

<system.webServer>
  . . .

  <caching>
    <profiles>
      <add extension=".php"
        policy="CacheForTimePeriod"
        kernelCachePolicy="DontCache"
        duration="00:01:00"
        varyByHeaders="Accept" />
    </profiles>
  </caching>
</system.webServer>
. . .

 

WCF REST サービスにおけるサーバー サイト キャッシュの構成

今回も、こちら で紹介した WCF Web Api の OrderService サービスをそのまま使用したいと思います。

補足 : 以降は、WCF Web API を使用していない WCF REST サービスにおいても、同様に設定できます。ただし、下記の通り、ASP.NET Compatibility モードに設定しておいてください。(ASP.NET の機能を使用するためです。)

<system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
. . .

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Service1
{
  . . .

まず、Web.config に、ASP.NET Cache のための構成を記述します。今回は、下記の通り記述します。(下記の通り、ここでは、60 秒間、Output データがキャッシュされます。)

. . .

<system.web>
  . . .

  <caching>
    <outputCache enableOutputCache="true"/>
    <outputCacheSettings>
      <outputCacheProfiles>
        <add name="myCacheProfile"
             location="Server"
             duration="60"/>
      </outputCacheProfiles>
    </outputCacheSettings>
  </caching>
</system.web>
. . .

あとは、WCF REST サービス上で、上記の myCacheProfile のプロファイルを使って ASP.NET キャッシュを使用するようにします。こちら で作成した GetOne オペレーションに、下記の通り、AspNetCacheProfile 属性 (Attribute) を設定するだけです。

. . .

[OperationContract]
[WebGet(UriTemplate = "{id}")]
[AspNetCacheProfile("myCacheProfile")]
public HttpResponseMessage GetOne(string id)
{
  . . .

補足 : 「Web Api (REST サービス) の Custom HTTP Header (HTTP ヘッダー)」では「カスタムの HTTP Header を試す」という目的のため、強引に Expires ヘッダーを設定しましたが、実は、上記で location="Client" と設定すると、クライアント キャッシュが使用できるようになります。HTTP レスポンス ヘッダーには、下記の通り設定されます。(下記は、duration を 60 に設定した場合のヘッダー情報です。)

Cache-Control: private, max-age=60
Expires: Mon, 26 Sep 2011 07:44:19 GMT (左記は、1 分後の値)
. . .

 

条件 (Query string, etc) に応じたキャッシュの制御

WCF REST サービスの場合、上記のままでは、まだ不完全です。
例えば、GetOne メソッド (こちら を参照) のように、パラメーター「id」の値によって出力結果が異なる場合、このパラメーターによってキャッシュをわける必要があります。また、GetOne メソッドでは Plain XML しか返していませんが、フォーマットによって複数の返り値を返す場合、「Plain XML の場合」、「Json の場合」など、Accept ヘッダーの内容によってキャッシュをわけておく必要もあります。
このため、Web.config に、下記 太字の通り追記して、パラメーターやヘッダーの内容によってキャッシュを分離するように構成しておきます。

<system.web>
  . . .

  <caching>
    <outputCache enableOutputCache="true"/>
    <outputCacheSettings>
      <outputCacheProfiles>
        <add name="myCacheProfile"
             location="Server"
             duration="60"
             varyByParam="id"
             varyByHeader="Accept"/>
      </outputCacheProfiles>
    </outputCacheSettings>
  </caching>
</system.web>
. . .

補足 : パラメーターやヘッダーを複数指定する場合は、varyByParam="category;id" のようにセミコロンで区切ります。

以上で完了です。

ブラウザーなどを使って動作確認すると、サーバー側でキャッシュがおこなわれ、下図で、1 分間 (60 秒間) 同じ時刻が表示されるようになります。(さらに、Fiddler などで確認していただくと、毎回、サーバー側に要求を出して、同じ Response が返ってくることが確認できます。前回のクライアント側のキャッシュの時とは、動作が異なります。)

 

さらに応用的なテーマ

例えば、Cookie の個々の値によるキャッシュ コントロールなど、パラメーター、ヘッダー以外の内容で独自にキャッシュ制御をおこなう場合は、varyByCustom 属性を使用できます。(この場合、Global.asax の GetVaryByCustomString オーバーライド メソッドを実装します。)

また、ご存じの方も多いと思いますが、Windows Azure / Windows Server では AppFabric cache という仕組みが利用可能で、ASP.NET Cache のプロバイダーとして、この AppFabric cache をバックエンドで使用できます。(使用方法は、「Code Recipe : Windows Azure AppFabric Caching を ASP.NET Output cache として使用する」に記載されていますので参照してください。) この仕組みを使用すると、Windows Azure 上で複数インスタンスを構成した場合や、Windows Server 上で負荷分散 (Load Balancing) を構成した場合、異なるインスタンス間で出力キャッシュのメモリー間レプリケーションが実現できます。

さらに、Cache Dependency を組み合わせる場合、ASP.NET Cache の SqlCacheDependency を使用して、SQL Server 上の特定のテーブルなどが更新された際にキャッシュの廃棄をおこなうことも可能です。ただし、上記の AppFabric cache には cache dependency の仕組みは存在しません。また、ASP.NET Cache では、SQL Server 以外に、カスタムの CacheDependency クラスを使って廃棄方針を独自にプログラミングできますが、SqlCacheDependency のように構成ファイル (.config) 上で指定できないため、実際、こうした Custom CacheDependency と組み合わせる場合には実装上の判断や高度な設計が必要となります。(例えば、WCF の実装コードの内部で ASP.NET Cache への保存・抽出の処理を記述する場合には使用できますが、上記のように構成や属性によって設定することはできません。)

補足 : なお、上述の Kernel Mode Cache / User Mode Cache にも、単純なファイルの更新通知を利用したキャッシュの動的更新 (File Change Notification) の仕組みを使用できます。(静的ファイルや、ASP スクリプトなどで使用できます。)

 

REST という HTTP に準拠したシンプルな接続スタイルだからこそ、上記のような柔軟なチューニングが可能となります。是非、適材適所に活用してみてください。

 

 


Comments (0)

Skip to main content