ニッキ

YAPC::Asia 2012行ってきた

[] 平均レスポンスタイム50msをPerlで捌く中規模サービスの実装/運用

基本的にはWeb+DB Press Vol70に書いているものの補足

IOをとにかく減らす

  • SSDを使う
  • DNS, RDBへの問い合わせをなくす
  • 極力/etc/hostsに書く

Profile

* upstream response time

  • -d:NYTProf
  • SIG{ALERM} でスナップショット取る
  • CloudForecast
  • GrowthForecast

Test/CI

サーバ

[] Plack::Middleware::InteractiveDebuggerがすごい

plackup -e 'enable "InteractiveDebugger"' -a app.psgi で使える。

stacktraceを差し替えるモジュールなんだけど、stacktrace中で変数のdumpとかできる

StackTrace (most recent call first)
File "local/lib/perl5/Plack/Middleware/InteractiveDebugger.pm", line 108, in Plack::Middleware::InteractiveDebugger::__ANON__
$self->app->($env);
[console ready]
perl> use Data::Dumper
perl> Dumper $env
$VAR1 = {
          'psgi.multiprocess' => '',
          'SCRIPT_NAME' => '',
          'SERVER_NAME' => 0,
          'HTTP_ACCEPT_ENCODING' => 'gzip,deflate,sdch',
          'HTTP_CONNECTION' => 'keep-alive',
          'PATH_INFO' => '/die',
          -- snip --

minicpanを工夫してみた

[]minicpanを工夫してみた

外出時にcpanモジュール入れるのにminicpanを使っていますが、いちいちminicpanを起動して、オプション指定して、使い終わったら落とすという流れがめんどくさかったので、一連の流れをコマンドにしてみた。

(minicpan用のhttpdを常時起動しておいて、PERL_CPANM_OPTを指定しておけばいいだけですけど)

minicpanの保存先を予め決めておいて

minicpan -l ~/perl5/minicpan -r http://mirror/

こんな感じでcpanm_minicpanとして作成した。

#!/usr/bin/env perl
use strict;
use warnings;
use Plack::Builder;
use Plack::Middleware::Static;
use Plack::Runner;
use Test::TCP;

my $server = Test::TCP->new (
    code => sub {
        my $port = shift;
        my $app = builder {
            enable "Plack::Middleware::Static",
            path => qr{^/},
            root => $ENV{HOME} . '/perl5/minicpan';
        };
        my @args = ("--port=$port");
        unless ( $ENV{DEBUG} ) {
            push @args, '--access-log=/dev/null';
            open STDERR, '>', '/dev/null' or die $!;
            open STDOUT, '>', '/dev/null' or die $!;
        }
        my $runner = Plack::Runner->new;
        $runner->parse_options(@args);
        $runner->run($app);
    },
);

printf STDERR "Start minicpan server: 0.0.0.0:%s (pid: %d)\n\n", $server->port, $server->pid;
my $port = $server->port;
system 'cpanm', '--mirror-only', "--mirror=http://localhost:$port/", @ARGV;

printf STDERR "\nStopping minicpan server: 0.0.0.0:%s (pid: %d)", $server->port, $server->pid;
undef $server;

zsh functionの_cpanmの補完対象に追加しておけば、補完も効く。

[] perlbrewのproxy対応

perlbrew (http://search.cpan.org/~gugod/App-perlbrew/)を教えてもらって便利そうなので使ってみたけど、proxy対応していなかったので、環境変数から呼ぶようにしてみました。

これで環境変数にhttp_proxyがあればproxy使ってくれます。

もう少し真面目に書いてCPANにパッチ送ろうかな。。。

注意点としては、HTTP::Liteのproxyメソッドが、http://host:port/ のフォーマットを前提にしているので、最後にスラッシュがないとうまく動きません。(昔yumもそんなんだったな。。。)

$ diff -ub perlbrew.orig perlbrew

--- perlbrew.orig       2010-05-31 18:36:45.630114803 +0900
+++ perlbrew    2010-05-31 18:40:10.086114474 +0900
@@ -922,6 +922,7 @@
             my $http_get = sub {
                 my ($url, $cb) = @_;
                 my $ua = HTTP::Lite->new;
+                $ua->proxy if exists $ENV{http_proxy};
 
                 my $loc = $url;
                 my $status = $ua->request($loc) or die "Fail to get $loc";

[] WWW::Mechanize + Proxy 認証

某サイトからデータ引っ張ってくるためにWWW::MechanizeとWeb::Scraperでコーディングした。

Mech + Proxy認証でハマったところ。。。

clone
my $mech_clone = $mech->clone

上記でcloneを作成すると、Proxyの設定はしてくれない。

そのため、元々のオブジェクトにおいてproxyメソッドでproxyを設定した場合は、プロキシを利用しない。

また、環境変数を設定しておきnewでenv_proxyを呼ばせて自動設定させた場合は、proxy認証部分(http://foo:bar@proxyhost:proxyportのfoo:bar部分)にアクセスしようとする。

実際の動作としては、$mech->{handlers}->{request_preprepare}にLWP::UserAgent::Proxyオブジェクトが入らない。

これは、$mech->cloneではLWP::UserAgentのcloneメソッドを利用している(下記参照)のでLWPのバグor仕様っぽい。

use base 'LWP::UserAgent';

--snip--

sub clone {
    my $self  = shift;
    my $clone = $self->SUPER::clone();

    $clone->cookie_jar( $self->cookie_jar );

    return $clone;
}

cloneするのにいちいちproxyを設定しなおすのも嫌なのでmechがproxyを持っていたら再度設定するように書き直した。

こんな感じ。


sub mech_fixup {
    package WWW::Mechanize;
    no warnings 'redefine';
    *clone = sub {
        my $self  = shift;
        my $clone = $self->SUPER::clone();

        $clone->cookie_jar( $self->cookie_jar );

        if(exists $self->{proxy}){
            while(my ($k, $v) = each %{$self->{proxy}}){
                $clone->proxy([$k],$v);
            }
        }
        return $clone;
    }
}


Web::Scraperのドキュメント

Podが充実してないので若干使いにくい。

ぐぐれば山ほど出てくるし、ソース読めばいいとはいえPod充実させてほしいなぁ

デス

[]open関数とUTF8フラグとRedHatLinux9と

Perlのバージョンが5.8.0で、RedHatLinuxのデフォルトのlocaleがen_US.UTF-8になってるとopen関数で開いたファイルに自動的にUTF8フラグをつけようとするという罠があることがわかった。

ま、3引数のopen関数を使うか、Encodeモジュールをきちんと使うか、Localeをきちんと設定しておけば起きないんですが。

教訓としては、

  1. Perlのバージョンは最新にしとけ
  2. 2引数のopenは使うな
  3. Encodeモジュール使え

ということで。。。

再現用プログラム

hoge.pl---

#!/usr/bin/perl -w

use strict;

use warnings;

use Jcode;

open(F,'hoge.txt');

my @line = <F>;

close F;

print Jcode->new(@line,'euc')->jis;

--------------

hoge.txt---

ほげ

--------------

hoge.txt : 文字コード EUC-JP

改行コード LF

再現可能な環境
  • Perl version : 5.8.0
  • Jcode : 0.83
  • OS : Red Hat Linux 9
OSの文字コードの設定

/etc/sysconfig/i18n

---------------------------------

LANG="en_US.UTF-8"

SUPPORTED="en_US.UTF-8:en_US:en"

SYSFONT="latarcyrheb-sun16"

---------------------------------