CPAN
前々から疑問を持っていたCPANについての疑問を解消した。
一般ユーザ権限でのCPANの使用
要はroot権限以下では/usr/lib/perl5あたりにインストールされるのを、自分のホームディレクトリ以下に置けばいい。
というわけで、/usr/lib/perl5に相当するディレクトリを掘る。
mkdir ~/perl-lib
次にcpan用の設定ファイルをあらかじめ、必要な部分だけ作っておく。
mkdir -p .cpan/CPAN vi .cpan/CPAN/MyConfig.pm
MyConfig.pmの中身は以下の通り。
$CPAN::Config->{cpan_home} = undef; $CPAN::Config->{makepl_arg} = 'PREFIX=~/perl-lib'; $CPAN::Config->{histfile} = "$ENV{HOME}/.cpan/histfile"; 1;
あとは通常通りの使用方法でOK。
今後書くスクリプトには
use lib qw(/home/crazycamel/perl-lib/lib/perl5/site_perl/5.8.8);
みたいなことをしておかなくてはいけない。
CPANのインストールでこけたら試してみること。
1.言語設定を変えてやってみる。
LANG=C perl -MCPAN -e shell
2.Makeしてみる。
cpan> install HTML::Entities Running install for module HTML::Entities Running make for G/GA/GAAS/HTML-Parser-3.56.tar.gz Is already unwrapped into directory /home/urapico/.cpan/build/HTML-Parser-3.56 Has already been processed within this session Running make test Can't test without successful make Running make install make had returned bad status, install seems impossible cpan> look HTML::Entities $ make install
3.force installしてみる。
cpan> force install モジュール名
参考文献
http://chain.main.jp/weblog/archives/2005/06/cpaninstall.html
http://d.hatena.ne.jp/urapico/20071224/1198513404
quotemeta \Q
#!/usr/bin/perl use strict; use warnings; my $string = 'foo ^bar baz'; my $regex = '^bar'; $string =~ s/$regex//; print $string . "\n";
実行結果
foo ^bar baz
#!/usr/bin/perl use strict; use warnings; my $string = 'foo ^bar baz'; my $regex = '^bar'; $string =~ s/\Q$regex//; print $string . "\n";
実行結果
foo baz
ハッシュのリファレンスに持ったファイルハンドルを処理するとき
そういうときは<>演算子じゃなくて、readlineだよってお話。
#!/usr/bin/perl use strict; use warnings; our $test = './test.txt'; my $hashref = fh_open($test); while(<$hashref->{handle}>){ print; } exit(); sub fh_open{ my $filename = shift; open my $handle, '<', $filename or die $!; return { file => $filename, handle => $handle }; }
実行結果
syntax error at ./handle.pl line 8, near "<$hashref->{handle" Execution of ./handle.pl aborted due to compilation errors.
#!/usr/bin/perl use strict; use warnings; our $test = './test.txt'; my $hashref = fh_open($test); while(readline($hashref->{handle})){ print; } exit(); sub fh_open{ my $filename = shift; open my $handle, '<', $filename or die $!; return { file => $filename, handle => $handle }; }
実行結果
I am Test File haha
Gearmanによるキューイング
キューイングを使うメリット
ウェブアプリケーションを対象として考えています。
処理を分別できる。
今すぐ必要な処理、後回しにして良い処理、負荷が高い処理、負荷が低い処理を分別することができます。
ユーザがすぐに確認しなければならない処理、負荷が低くシステムの負担にならない処理は自分自身で処理し、後回しにして良い処理、負荷が高い処理はサーバにお願いして、違う人にやってもらえば、自分の仕事は極端に減ります。(ユーザにとって快適なウェブアプリケーションとなる。)
サーバプロセスがロードするライブラリが減る。
ウェブアプリケーションのプロセスないで重い処理を簡潔させてしまうと、待機プロセスすべてが、その重い処理に必要なモジュールやライブラリを読み込ませているので、その分メモリを占有してしまうらしいです。
これを別のプロセスで行えば、HTTPサーバ待機プロセスは軽くなります。
キューイングシステムの基本的な構成
+--------+ +--------+ +--------+ | Client | 仕事の依頼-> | Server | <-仕事を引き受ける | Worker | +--------+ +--------+ +--------+
ウェブアプリケーションで考えます。
ClientはCGIでしょう。Serverは今回はGearmanを使います。Workerは処理によって定義されます。
とりえあずインストール
FreeBSD
#cd /usr/ports/devel/p5-Gearman-Server #make install clean
起動
# gearmand --daemonize --pidfile=/var/run/gearmand.pid --port=7003
エラーでた。。。
/usr/local/bin/gearmandの先頭行が#!/usr/bin/local/perlになってた。
#!/usr/bin/perlに直して再度試すと、無事起動しました。
起動時のパラメータについて
- daemonize
- オプションを付けると個プロセスがforkされてデーモンとして稼働します。
- pidfile
- プロセスIDをファイルに記録することことができます
- port
- gearmandはデフォルトでポート7003番で稼働します。ひとつのサーバで複数のgearmandを立ち上げるにはポート番号を変更する必要があります。
gearmandサーバ停止方法
kill `cat /var/run/gearmand.pid`
テストWorker
#!/usr/bin/perl #gearmanテスト用ワーカー:) use strict; use warnings; use Gearman::Worker; use Storable qw(freeze thaw); use LWP::UserAgent; #ワーカーのインスタンス生成 my $worker = Gearman::Worker->new; # gearmandが稼働するサーバを指定 $worker->job_servers(qw/localhost:7003/); my $ua = LWP::UserAgent->new(timeout => 1); # get_site_statusジョブをサーバに登録 $worker->register_function( get_site_code=> sub{ my $job = shift; my %result = (); my @args = @{thaw($job->arg)}; for my $url (@args){ my $res = $ua->get($url); next unless $res->is_success; $result{$url} = $res->code; } return freeze(\%result); } ); # 処理待ちループ $worker->work while 1;
テストClient
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Storable qw(freeze thaw); use Gearman::Client; my $client = Gearman::Client->new; $client->job_servers(qw/localhost:7003/); my @args = ( 'http://www.perl.org', 'http://cpan.org', 'http://c2.com/cgi/wiki', ); my $args = freeze(\@args); my %option = (); #ジョブを登録してその結果を受け取る my $result = $client->do_task(get_site_code => $args, \%option); my $deserialized = thaw($$result); print Dumper $deserialized;
イントロスペクション
Perlはグローバルな(myで宣言していない)変数のルックアップにはシンボルテーブルが使用されます。
シンボルテーブルとはハッシュであり、キーと値の対の集合です。
シンボルテーブルのキーとは変数名であり、値は変数名のグロブです。
例えば以下のようなコードを実行した場合
#!/usr/bin/perl use strict; use warnings; our $a;
シンボルテーブルは以下のようになります。
キー | 値へのリファレンス |
---|---|
a | グロブa |
なぜ、グロブが出てくるのか?
それはPerlにとってaという単語は様々な意味を持つからです。
Perlが$aの値を見つけるには2ステップ必要です。
- シンボリックテーブルからaを探し、グロブaを見つける。
- グロブaから$aを見つける。
ということはグロブを書き換えてやれば、変数の別名(エイリアス)を作ることができます。
#!/usr/bin/perl use strict; use warnings; our $a = 10; *b = *a; print $b . "\n";
実行結果は以下です。
10
これはあくまで、グローバル変数の世界の話しであって、my宣言されたものは動きません。
#!/usr/bin/perl use strict; use warnings; my $a = 10; *b = *a; print $b . "\n";
実行結果
Name "main::a" used only once: possible typo at ./introspection.pl line 6. Use of uninitialized value in concatenation (.) or string at ./introspection.pl line 7.
上記のエラーメッセージはwarningsプラグマせいです。warningsプラグマをはずすと、なにも表示されません。
だって、$bは$aではないからね。
偽Exporterを作る。
Exporterの動作を確認するため、ふたつコードを書いてみた。
TestModule.pm
package TestModule; use strict; use warnings; use base 'Exporter'; our @EXPORT = qw(camel); sub camel($){ my $msg = shift; print "Camel says $msg\n"; } 1;
import.pl
#!/usr/bin/perl use strict; use warnings; use TestModule; camel("I am Crazy.")
import.plの実行結果は以下になる。
Camel says I am Crazy.
上記の場合Expoterはimport.plにTestModule.pmのcamelメソッドをインポートする責任がある。
これっぽい動きをするのが以下。
TestModule.pm
package TestModule; use strict; use warnings; sub camel($){ my $msg = shift; print "Camel says $msg\n"; } sub import{ no strict 'refs'; *{caller()."::camel"} = *camel; } BEGIN{ &import; } 1;
import.plの実行結果は以下になる。
Camel says I am Crazy.
TestModule.pmを呼び出したとたんに、importメソッドを呼ぶ。
importメソッドはcaller()を読んで、main::camelという文字列を作成する。
no strict 'refs'によってこの構文はglobとして解釈される。
main::camelグロブはcamelグロブによって上書きされる。
これによってimport.pl内でのcamelサーブルーチン呼び出しは、TestModule::camelメソッドを呼ぶことになる。
問題点
しかしこれでは、$camel,@camel,%camel,ファイルハンドルcamel,ディレクトリハンドルcamelもTestModule.pmのものを使ってしまいます。
TestModule.pm
package TestModule; use strict; use warnings; our $camel = 'I live in TestModule.pm'; sub camel($){ my $msg = shift; print "Camel says $msg\n"; } sub import{ no strict 'refs'; *{caller()."::camel"} = *camel; } BEGIN{ &import; } 1;
import.pl
#!/usr/bin/perl use strict; use warnings; use TestModule; camel("I am Lazy."); print $camel . "\n";
import.plの実行結果は以下です。
Camel says I am Lazy. I live in TestModule.pm
僕はサブルーチンだけを書き換えたいのです。
そんな方法をPerlはもちろん持っています。
# サブルーチンだけ書き換える。 *glob = \&camel; # スカラーだけ書き換える。 *glob = \$camel; # 配列だけ書き換える。 *glob = \@camel; # ハッシュだけ書き換える。 *glob = \%camel;
#!/usr/bin/perl use strict; use warnings; our $camel; our @camel; our %camel; *camel = \"Hello"; *camel = [1..10]; *camel = { 'a' => 'a', 'b' => 'b'}; print $camel . "\n"; print @camel . "\n"; print $camel{a} . "\n";
実行結果は以下です。
Hello 10 a
これで好きならくだにアイリアスする方法を見つけました。
よってTestModule.pmは以下のようにすればサブルーチンだけエイリアスできます。
package TestModule; use strict; use warnings; our $camel = 'I live in TestModule.pm'; sub camel($){ my $msg = shift; print "Camel says $msg\n"; } sub import{ no strict 'refs'; *{caller()."::camel"} = \&camel; } BEGIN{ &import; } 1;