― Web Technology and Life ―

PerlのウェブアプリケーションフレームワークAmon2でオブジェクトキャッシュ

2011-09-15
PerlのWAF(ウェブアプリケーションフレームワーク)のPicklesとかKamuiだとContainerというクラスがあって、設定情報をコンストラトしたオブジェクト(インスタンス)を登録してキャッシュしておけるけど、Amon2ではどういう方法があるかを考えたまとめです。

Containerを使うメリット

そもそもWAF内でサブクラス化したContainerを使うメリットは何かということを考えると以下の点になると思います。(他にあれば教えて下さい・・・m(_ _)m)

  • 都度インスタンスを生成するによって発生するCPUコスト、メモリの節約が可能
  • 共通で使用するインスタンスをまとめた一覧表ができる
  • インスタンスを使用するクラスで、個別に「use ~;use ~;」とか、「~new();~new();」とダラダラ書く必要がなくなる
  • Object::Containerとかの場合は、使用時にロードされるのでコンテナーにガンガンとインスタンスをいれても重くならない

個人的には、開発スピードアップに繋がる真ん中の2つのメリットが嬉しいです。

Amon2でオブジェクトキャッシュをするときは$cにメソッドをはやせばよい

Amon2で作ったMyApp(自分でつくろうとしているWebアプリの仮の名前)にContainerと同じものを実装しようとしたしようした場合、Object::Containerを使ってContainerを実装してもいいのですが、以下の2つの方法により、(最後のメリットを除いて)$cからメソッド呼び出してキャッシュしたオブジェクトにアクセス可能なものとして実装可能です。

【追記の補足】
コンテナー的に使えるのは、Amon2クラスにグローバル変数の$CONTEXTがいて、これが$cとなっているからです。グローバル変数の$cにメソッドキャッシュすることは、そのキャッシュされたインスタンスも$cから呼び出しているうちはグローバル変数ということになります。

  • MyApp、または、MyApp::Webの実装クラスにメソッド書く
  • Pluginを使ってメソッドをはやす

実装クラスにメソッド書く

以下、サンプルコードにあるようにAmon2を継承した実装クラス(MyApp、または、MyApp::Web)にオブジェクトをキャッシュしていくことができます。

Database Integration ― Amon2 v documentation

package MyApp;
use parent qw/Amon2/;
use Teng::Schema::Loader;
use Teng;

sub db {
    my $self = shift;
    if ( !defined $self->{db} ) {
        my $conf = $self->config->{'DB'}
          or die "missing configuration for 'DB'";
        my $dbh = DBI->connect($conf);
        my $schema = Teng::Schema::Loader->load(
            namespace => 'MyApp::DB',
            dbh       => $dbh,
        );
        $self->{db} = Teng->new(
            dbh    => $dbh,
            schema => $schema,
        );
    }
    return $self->{db};
}

1;

Pluginを使ってメソッドをはやす

上記の実装をPluginを使って行うこともできます。MyAppとMyApp::Web::Plugin::DB的なものを以下のように書きます。

package MyApp;
use strict;
use warnings;
use parent qw/Amon2/;
our $VERSION='0.01';
use 5.008001;

__PACKAGE__->load_plugins(qw/
    +MyApp::Plugin::DB
/);
1;
package MyApp::Plugin::DB;
use strict;
use warnings;
use Amon2::Util;
use Teng::Schema::Loader;
use Teng;

sub init {
    my ($class, $c) = @_;
    Amon2::Util::add_method(
        $c,
        'db',
        sub {
            my $self = shift;

            $self->{db} ||= do {
                my $conf = $self->config->{'DB'}
                  or die "missing configuration for 'DB'";
                my $dbh = DBI->connect($conf);
                my $schema = Teng::Schema::Loader->load(
                    namespace => 'MyApp::DB',
                    dbh       => $dbh,
                );
                Teng->new(
                    dbh    => $dbh,
                    schema => $schema,
                );
            }
        }
    );
}
1;

MyApp、または、MyApp::Webのどちらにキャッシュメソッドを書けばよいか?あるいは、Pluginをロードすればよいか?

MyApp、または、MyApp::Webのどちらにメソッドを書いても、$cから呼び出すことができますし、MyApp、または、MyApp::Webのどちらにload_pluginsでプラグインをロードしても$cから呼び出すことができます。それでは、MyApp、または、MyApp::Webのどちらにオブジェクトをキャッシュするメソッドを書けば良いのでしょうか?

そもそも、$cは、MyApp->bootstrapでコマンドラインで使うときは、MyAppクラスのオブジェクトとなり、MyApp::Web->to_appでウェブアプリとして使うときは、MyApp::Webクラスのオブジェクトとなります。MyApp::WebはMyAppを継承することになっていますので、$cが後者のMyApp::Webのオブジェクトになっているときでも、MyAppのメソッドにアクセス可能です。よって結論としては、キャッシュメソッド、あるいは、プラグインのロードは、ウェブアプリでしか使わないものについてはMyApp::Webに、コマンドラインでも使えるかもというものについてはMyAppにということになります。

$cをブラックボックス化させないように注意

MyAppをuseすれば、いたるところから$cのContextオブジェクトは呼び出せるので、$cは完全なるグローバル変数化されます。そして、$cにどんなメソッドがはやされているのか、よくわからなくなっていきます。小さいアプリの時は適当にやっていればいいのですが、ちょっと大きくなったときに突然振られた人はこの$cというブラックボックスクラスの取り扱いに困ること間違いなしです。なので、プラグインでメソッドを生やすかMyAppとかにメソッドを増やしていくかについては、基準を持って分けたほうがよいと思います。また、ここまで書いてきて何ですが、そこそこ規模が大きくなってきたら、やっぱりObject::Containerとか使って独自のContainerクラスを実装していったほうがわかりやすいと思います(笑

最後に

最近、「Amon2を~」的なのが流行っている感があると思ったので、せっかくコードを眺めたので、波に乗って一つネタをば、と思いまとめてみましたー。間違えとかこうできるよ!!っていうのがあれば、どしどしお願いします!!

Perl update_at : 2011-09-16T12:44:45
hirobanex.netの更新情報の取得
 RSSリーダーで購読する   
blog comments powered by Disqus