2013年9月24日火曜日

web2py 静的アセット管理

以前メーリングリストで、web2pyの「静的アセット管理」について書いたことがありますが、このブログでも少し内容を拡張して書いてみます。
静的アセット管理

静的アセット管理とは、静的ファイルをブラウザのキャッシュで利用する仕組みだ。Django でも同じような機能のコンポーネントがあるようだ。

具体的には次のように、モデル定義(db.pyなど)に記述する。

response.static_version = '1.1.1'

これによって次のように動作する。

  1. 静的ファイルのURL変更
  2. 静的ファイルのヘッダ情報の書き換え
  3. 対象の静的ファイルは、response.files に含まれているファイル
もう少し詳しく説明すると、
1. 静的ファイルのURL変更
例えば次のような静的ファイルのURLが、
http://127.0.0.1:8000/myapp/static/css/web2py.css
次のように変更される。
http://127.0.0.1:8000/myapp/static/_1.1.1/css/web2py.css
つまり、response.static_version で設定した値が、/_1.1.1のように静的ファイルのURLの一部に含まれるようになる。

2. 静的ファイルのヘッダ情報の書き換え
response.static_version の設定がある場合とない場合のヘッダ情報を比べると、次の項目が変更追加されていることがわかる。
Expires: Thu, 31 Dec 2037 23:59:59 GMT
Cache-Control: max-age=315360000
設定がない場合は、Expires の項目がなく、Cache-Control にも private が設定されている。これらの値が設定されることにより、静的ファイルがブラウザキャッシュから使用されるようになる。またキャッシュの有効期限が Expires の場合は2037年まで、Cache-Control は期間を10年で指定している。

3. 対象の静的ファイルは、response.files に含まれているファイル
response.static_version が適用されるファイルは、response.filesに指定したファイルになる。デフォルトでは次の静的ファイルが含まれる。
  • js/jquery.js
  • css/calendar.css
  • js/calendar.js
  • js/web2py.js
  • css/web2py.css
  • css/bootstrap.min.css
  • css/bootstrap-responsive.min.css
  • css/web2py_bootstrap.css
効果と注意点
ここでブラウザのキャッシュの中身を見てみよう。

静的アセット管理の対象ファイルのキャッシュ期限が、10年後の2023年に指定されていることが確認できる。

静的アセット管理を設定した場合、ブラウザキャッシュによる効果として、次の二点が考えられる。

  • ネットワーク負荷の削減
  • サーバ負荷の削減

もちろんブラウザキャッシュを使用するため、対象の静的ファイルが変更された場合など、本来サーバ上のファイルを使用すべきものが、ブラウザキャッシュのファイルを使用してしまう可能性がある。このため対象となる静的ファイルを変更した場合は、response.static_versio を変更する必要がある。例えば次のように変更する。

response.static_version = '1.1.2'

これによって静的ファイルのURLに含まれていた /_1.1.1 が、/_1.1.2 と変更されるため、新しいファイルとして認識される。

以後ファイルの変更の度に、response.static_version に指定する番号を、過去の番号と重複しないように変更する必要がある。

さらなる活用
静的アセット管理の仕組みは記述さえすれば、常にブラウザキャッシュを使用するため便利な設定であるが、デフォルトで対象となるファイルは限定されている。画像ファイルなどのキャッシュを利用すれば負荷を大きく削減できるファイルに適用するためには、次のように設定する。
URL('static', ('_'+response.static_version+'/' \
                     if response.static_version else '') +'images/test.jpg')

アプリケーションの static ディレクトリの image/test.jpg ファイルを、静的アセット管理で参照する。この記述を利用したコントローラ関数の例を次に示す。

def image():
   img = IMG(_src=URL('static', ('_'+response.static_version+'/' \
                        if response.static_version else '') +'images/test.jpg'))
   return dict(img=img)

このコントローラ関数を実行すると、image/test.jpg の画像を画面に表示する。

Google app engine での注意点

web2py 2.4.7 以前のバージョンでは、GAE上での静的アセット管理に対応していない。この場合、app.yaml の次の記述を、

- url: /(?P<a>.+?)/static/(?P<b>.+)
  static_files: applications/\1/static/\2
  upload: applications/(.+?)/static/(.+)
  secure: optional

次のように変更することが必要だ。

- url: /(.+?)/static/_\d.\d.\d\/(.+)
  static_files: applications/\1/static/\2
  upload: applications/(.+?)/static/(.+)
  secure: optional
  expiration: "365d"

- url: /(.+?)/static/(.+)
  static_files: applications/\1/static/\2
  upload: applications/(.+?)/static/(.+)
  secure: optional
expiration でキャッシュ期限を一年で設定している。必要であれば、この値も変更ください。


静的アセット管理の仕組みを使用すれば、気軽にアプリケーションのチューニングを行えます。結構大規模なサイトでないと効果がハッキリ出ないかもしれませんが、容易に実施できるというのが利点です。