Craftworks Tech Blog - Branch このページをアンテナに追加 RSSフィード

2009-08-02

Catalyst で setup にかかった時間を表示する

| 13:22 | Catalyst で setup にかかった時間を表示する - Craftworks Tech Blog - Branch を含むブックマーク Catalyst で setup にかかった時間を表示する - Craftworks Tech Blog - Branch のブックマークコメント

Catalystデフォルトでリクエスト毎の処理時間をログ出力してくれますが、setup() 時の処理時間は表示してくれません。チューニングの目安として見たいので実装してみました。

Upgrading to Catalyst 5.80 で紹介されている、setup の hook point を使います。

package MyApp;

use Moose;
use Catalyst::Runtime 5.80;
use Catalyst;
use Time::HiRes;
extends 'Catalyst';                                                                                 

our $StartedOn;

BEGIN {
    $StartedOn = Time::HiRes::time;
}

after setup_finalize => sub {
    my $c = shift;
    $c->log->info(sprintf 'Setup took %0.6fs', Time::HiRes::time - $StartedOn );
    $c->log->_flush; # これがないと1回目のリクエストまで出力されない
};

__PACKAGE__->setup(qw/
    -Debug
/);

__PACKAGE__->meta->make_immutable;

こんな感じで表示されます。本家でも表示してくれるといいですね。

[info] MyApp powered by Catalyst 5.80007
[info] Setup took 0.656298s
FastCGI: manager (pid 23210): initialized
FastCGI: manager (pid 23210): server (pid 23211) started
FastCGI: server (pid 23211): initialized

2009-06-06

Catalyst を daemontools で監視しつつ lighttpd の外部 FastCGI で走らせる方法とそのメリット

| 22:44 | Catalyst を daemontools で監視しつつ lighttpd の外部 FastCGI で走らせる方法とそのメリット - Craftworks Tech Blog - Branch を含むブックマーク Catalyst を daemontools で監視しつつ lighttpd の外部 FastCGI で走らせる方法とそのメリット - Craftworks Tech Blog - Branch のブックマークコメント

JPA セミナーの時に jshery 氏も勧めていましたし、近頃 Geek の話題で目立つようになってきた、Catalystmod_perl でなく、外部 FCGI として走らせる設定方法を紹介します。

Catalyst プロセスの起動管理は DJB 氏の daemontools による管理がお勧めです。プロセスが死んでも自動的に再起動してくれます。手動での再起動も楽です。screen からショートカットキー登録して Catalyst再起動する方法も後ほど紹介します。

CatalystFastCGI 起動の設定

まずは daemontools の run スクリプトです。

run

#!/bin/sh
exec 2>&1
exec env - \
PATH='/bin:/usr/bin:/usr/local/bin:/var/www/www.example.com/script' \
DBIC_TRACE=1 \
CATALYST_DEBUG=1 \
DBIC_NO_WARN_BAD_PERL=1 \
setuidgid example \
myapp_fastcgi.pl -pidfile 'pid' -listen '/tmp/.s.www.example.com' -nproc 5 -keeperr \

ユーザー権限で Catalyst プロセスを走らせた方が色々と不便が無いので、 setuidgid を使っています。

次にログの run スクリプトです。multilog が自動的にログのローテートも面倒見てくれます。

log/run

#!/bin/sh
LOG=/var/log/www/www.example.com/
exec env - PATH='/usr/bin:/usr/local/bin' \
setuidgid example \
multilog s16777216 n4 \
-'* *Catalyst/View/ClearSilver.pm *' \
$LOG/all \
-'*' \
+'[debug] *' \
s4194304 n4 \
$LOG/debug \
-'*' \
+'[error] *' \
s4194304 n4 \
$LOG/fatal \
-'*' \
+'SELECT *' \
+'INSERT *' \
+'UPDATE *' \
+'DELETE *' \
s4194304 n4 \
$LOG/sql \

DBIC が吐くクエリログを別ディレクトリに分けています。

lighttpdFastCGI 設定

ログをチェックして Catalyst の起動ができたら、次に lighttpd の設定です。

lighttpd.conf

server.modules = (
    "mod_fastcgi",
)

$HTTP["host"] =~ "www\.example\.com" {
    # Static Files ===================== 
    server.document-root = "/var/www/www.example.com/static"
    # FastCGI ==========================
    $HTTP["url"] =~ "/[^/.]*(\?|$)" {
        fastcgi.server = (
            "" => ((
                "check-local" => "disable",
                "socket" => "/tmp/.s.www.example.com",
           ))
        )
    }
}

拡張子があるリクエストは lighttpdサーブさせて、拡張子がないリクエストは FastCGI のソケットに渡しています。

特定の拡張子Catalyst に渡したいときは、$HTTP["url"] の正規表現で調整します。

screen からショートカットキー一発で再起動

$HOME/.screenrc

# Send TERM signal to fcgi proccess
bind t exec bash -c 'cat /service/fcgi-www.example.com/pid | xargs kill -TERM'

ここで指定している /service/fcgi-www.example.com/pid は、Catalyst を起動するときに、myapp_fastcgi.pl -pidfile 'pid' で指定しているファイルです。

開発中にいちいち screen のウィンドウを root ユーザーのウィンドウに切り替えて、

svc -t /service/fcgi-www.example.com

と打つのも面倒臭いので、svcroot じゃないと実行できませんが、Catalyst プロセス自体は setuidgid で既にユーザー権限で走っているので、screen のショートカットキーに登録して楽に再起動できるようにしています。

外部 FastCGI のメリット

http://angelos.g.hatena.ne.jp/dann/20090603/1244029646

 少し調べてみて思ったのですが、多量のアクセスを捌くような必要があるケースでは、現状ではmod_perlでやったほうが楽なのかなぁという気がしました。これはApacheプロセス管理をまかせられるからで、プロセスの管理をFCGI::ProcManagerなどでやるのはどうなんだろうなぁと思ったからでした。FastCGIの明白な利点というのがいまいち理解できなかったというのが正直なところで、FastCGIに乗り換える理由がいまはよくわからないなぁという印象です。

 FastCGIのほうがいいという人もいるわけで、何か見落としているような気もします... 引き続きもう少しFastCGIを使ってみようかなぁと思っているので、ここがFastCGIを使う事の最大のメリット!というのがあれば、是非教えてください。

FastCGI の最大のメリットは以下だと考えています。

後者が重要で、例えば Catalyst をバージョン 5.7 で動かしたいアプリと、5.8 の Catamoose で動かしたいアプリと、同じ Web サーバーで複数のアプリを立ち上げようとしても、mod_perl では出来ません。外部 FastCGI の場合、ひとつのアプリが独立したひとつのプロセスとして走るので、各モジュールもそれぞれのプロセスのメモリの中にロードされますので、好きなモジュールの好きなバージョンを使えます。この辺は local::lib の使用とも絡んでくると思います。

danndann 2009/06/07 00:47 「mod_perl のように、メモリ上で名前空間がぶつからない。」というのが差だとすると、プロセスわければいいだけなので、あまり変わらないかなぁと。
See also: http://yusukebe.com/archives/09/05/26/124559.html

2009-05-31

Catalyst ベースのアプリケーション設計(構想)

| 11:38 | Catalyst ベースのアプリケーション設計(構想) - Craftworks Tech Blog - Branch を含むブックマーク Catalyst ベースのアプリケーション設計(構想) - Craftworks Tech Blog - Branch のブックマークコメント

Catalyst は Web アプリケーションを開発するときに、フレームワークとしてとても便利です。

ひとつの Web サービスを作ろうとすると、以下のような様々な処理が必要になってきます。

これを、すべて Catalyst にやらせてしまうと、プロセスが太りすぎてしまいますし、アプリケーション設計として不自然です。

結論から先に言ってしまうと、モデルやロジックだけでなく、ヴァリデーション処理なども Catalyst から分離してしまい、Catalyst はリクエストのディスパッチと View だけを担当するのがスマートで、拡張・メンテナンス・テストがしやすいやり方なのかなと、最近うっすらと考えています。Catalyst 在りきではなく、アプリケーションの一部分を Catalyst が担うといった感じですね。

本題とは逸れますが、現在 mst 氏が Reaction という Catalyst ベースのフレームワークを開発中のようなので、また新しい動きが期待されます。

Catalyst を薄くするという点については、jshirley 氏も Catalyst の Controller は薄ければ薄いほど良いと言っています。*1


そこで、Catalyst を使って何個かアプリケーションを作ってるうちに、なんとなく辿り着いた(今のところ)クラスの分け方を以下に紹介します。

ユーティリティクラス
path_to() など、ユーティリティ関数もろもろ。自前で実装。Catalyst から分離。
コンフィグクラス
全てのクラスから使えるように、Catalsyt から分離。自前で実装。Catalyst::Plugin::ConfigLoader を参考に作成。データベースアクセスクラスや API クラス、Catalsyt から呼べるようにします。実装については別エントリに書きます。
ロギングクラス(Log::Dispatch / Log::Log4perl など)
全てのクラスから使うログ出力用クラス。
データベースアクセスクラス
なんらかのキーを受け取って、ハンドラや O/R マッパーのインスタンスを返します。マスター/スレーブ構成や複数コネクションへの対応はここでします。呼び出し側で接続先を気にしなくも済むようなインターフェイスを用意してあげます。
CRUD 処理クラス
データベースアクセスと API の中間層です。データベースアクセスクラスのインスタンスをメンバに持ち、各エンティティの CRUD 処理を担当します。渡されたデータ形式のチェックと、登録・読込・更新・削除だけしかしません。その他のいかなるロジックも持たせません。データを直接いじるのはこのクラスだけです。
API クラス
CRUD 処理クラスのインスタンスを持ちます。CRUD 処理クラスの各メソッドを叩き、ロジック部分を担当します。コマンドラインプログラムCatalyst と両方から呼ばれます。出来る限りブラックボックス化します。
ウェブインターフェイスクラス(Catalyst)
Catalyst の担当部分です。Catalyst::Model::MultiAdaptor を使って APIインスタンスに持ちます。主にディスパッチ、認証、セッション、View などを担当します。コントローラーはとにかく薄くし、ロジックAPI に追い出します。
コマンドラインインターフェイスクラス(App::Cmd / Moose::App::Cmd など)
管理用のプログラムや、cron から実行する処理などを担当します。
ジョブキュークラス(TheSchwartz など)
メール送信、画像変換など、時間のかかる処理を Catalyst でやってしまうと、送信先の SMTP サーバーが重かったりすると、ブラウザのレスポンスが遅くなってしまいますし、FCGIプロセスが占有されてしまいますので、バックグラウンドで非同期に行い、処理の終了時になんならかの通知を出す、という実装にします。

と、大体このような感じです。変な部分や、「こうした方がもっといいよ」などありましたら、ご意見いただければと思います。

[追記 06/02]
ロギングクラスを忘れていたので加筆しました。

[追記 06/03]
データベースアクセスクラスではなく、データアクセスクラスにして、その子クラスでデータベースやファイルなど色々なストレージに対応出来るようにした方が良いですね。

2009-02-27

Catalyst の LocalRegex は定義順を見る

| 14:56 | Catalyst の LocalRegex は定義順を見る - Craftworks Tech Blog - Branch を含むブックマーク Catalyst の LocalRegex は定義順を見る - Craftworks Tech Blog - Branch のブックマークコメント

色々試して原因を特定するのに 2 時間以上ハマってしまいました。

package MyApp::Controller::Foo;
sub bar : LocalRegex('(\w+)')
sub baz : LocalRegex('^baz')

だと、/foo/baz は bar() にマッチ

package MyApp::Controller::Foo;
sub baz : LocalRegex('^baz')
sub bar : LocalRegex('(\w+)')

だと、/foo/baz は baz() にマッチ

だから、ユーザー情報を扱うコントローラーとかで、login(), register() など、予約された URL 以外はユーザー情報を表示とかしたいときに、

sub register : Local

は Regex(), LocalRegex() より優先されるから、どこで定義しても良いが、

sub foo : LocalRegex('^foo/(\w+)')

は、

sub show : LocalRegex('^(\w+)')

よりも先に定義しないといけない。

2009-02-24

オートログインの実装方法

| 17:03 | オートログインの実装方法 - Craftworks Tech Blog - Branch を含むブックマーク オートログインの実装方法 - Craftworks Tech Blog - Branch のブックマークコメント

Catalyst で「ログイン状態を記憶する」のようなオートログイン機能を実装する方法をメインのブログに公開しました。

Catalyst でオートログインとブラウザを閉じるまで有効な Cookie を共存させる

Catalyst::Plugin::Session::DynamicExpiry を使いつつ、Catalyst::Plugin::Session::State::Cookie のメソッドをオーバーライドして、セッションと Cookie の両方の有効期限を動的に変更しています。