2013年06月11日

Yappo::proto is not inline expansion?
in perl 5.18.0
本来なら Dump(Yappo::proto); となってる部分はコンパイル時に Dump(1); という解釈されてるので my $x = Yappo::proto . ''; のところで Yappo::proto の中身に PV の値が入ってても関係無いはずなのに実際は関係ある。みたいな変な挙動になってる。 というか定数なのに内部的に文字列の "1" が追加されちゃってる。
Posted by Yappo at 19:14 | Comments (0) | TrackBack (0) | Perl

2013年06月07日

一般的な Web Application というのはロードバランサ、Webサーバ、アプリケーションサーバという HTTP を喋るサーバで構成されていると思います。
ロードバランサは高級なハードウェアからソフトウェア(lvs, httpd, etc..)で作るものまで色々ありますね。
アプリケーションサーバでは各種言語に合わせた実装でデーモンが常駐してるでしょう。これはいわゆる普通の Web サーバよりは単純なコンテンツを返す性能が低いです。

そんなわけで動的なアプリケーションサーバが有る構成では js や css や画像など静的なファイルは Apache や nginx などの専用の Web サーバでサービスして、動的なリクエストだけバックエンドのアプリケーションを返す構成を取ります。
構成としては

構成A
  +---------------+
  | reverse proxy |
  +---------------+
          /\
         /  \
        /    \
       /      \
+--------+  +---------+ 
| static |  | dynamyc |
+--------+  +---------+ 
とか
構成B
+------------------------------------+
| static contents with reverse proxy | (Web Server)
+------------------------------------+
                  |
         +------------------+ 
         | dynamyc contents | (application server)
         +------------------+ 
のような二通りの構成が取れると思いますが、このエントリでは後者のシンプルな方で話を進めます。
そしてアプリケーションの種類によっては Web サーバとアプリケーションサーバを一つのインスタンスに同居させる事もあるでしょう。

そして冗長性を考えると、それぞれのパーツが1台づつで運用されてるとかマジありえないので普通のサービスの最小セットは以下のようになります。

構成C
+----------+ +----------+
| balancer | | balancer |
+----------+ +----------+
     |     \  /   |
     |      \/    |
     |      /\    |
     |     /  \   |
+----------+ +----------+
|   web    | |    web   | (Web サーバ)
|    |     | |     |    |
|   app    | |    app   | (アプリケーションサーバ)
+----------+ +----------+

app が落ちたらアウト

上の構成の場合だと app が落ちたら前段の web が 502 を返してしまって、折角 balancer が良い感じにバランシングしてくれてるのに可用性が下がってしまいます。

注意深く balancer をセットアップしていれば、 app のダウンを検知して故障のある app を抱えてる web をはずす事は出来ますが, 検知に時間はかかるし構成にかかる設定が複雑になります。

特にアプリケーションサーバの再起動を伴う作業の時に一瞬止まったりする可能性のあるアプリケーションサーバを使った場合には、再起動のたびに 502 を連発してしまってダウンタイム多発になります。
(この問題は近代的な環境である Unicorn や Server::Sterter などを使う事によりホットデプロイで対応可能になっています。)

app が落ちても平気にする

この問題を解決する手段としては web と app の間で更で以下のようにしてバランシングしてあげるという事です。

構成D
+----------+ +----------+
| balancer | | balancer |
+----------+ +----------+
     |     \  /   |
     |      \/    |
     |      /\    |
     |     /  \   |
+-----------+ +-------------+
|   web (A) | |    web (B)  | (Web サーバ)
+-----------+ +-------------+
     |     \  /   |
     |      \/    |
     |      /\    |
     |     /  \   |
+-----------+ +-------------+
|   app (A) | |    app (B)  | (アプリケーションサーバ) 
+-----------+ +-------------+

# web (A) と app (A) ならびに web (B) と app (B) はそれぞれ同一インスタンス
web サーバが Apache だったり nginx だったりする時は、良い感じにバランシングしながら後ろの web サーバに reverse proxy する方法があるので捗ります。

ただしこの手法でもバランシングの設定が必要になるので、サーバクラスタに web(C) app(C) を追加して balancer で web(C) を追加して満足して httpd.conf などに app(C) を追加し忘れて分散してるつもりが負荷が偏る悲しい問題も発生し得ます。

httpd.conf にちゃんと追加さえすれば、同じサービスの全部の設定は一度書けばかってにバラまかれて良い感じに再起動されるので balancer 側で頑張るよりはだいぶ柔軟な運用ができるがそれは後ほど。

特殊な環境の場合

たとえば JSON API しかサービスしないよ!っていうケースの場合は静的コンテンツをサービスする為の Web サーバが実質不要になるため balancer と app だけの以下の構成を取るかもしれません。

構成E
+----------+ +----------+
| balancer | | balancer |
+----------+ +----------+
     |     \  /   |
     |      \/    |
     |      /\    |
     |     /  \   |
+-----------+ +-------------+
|   app (A) | |    app (B)  | (アプリケーションサーバ) 
+-----------+ +-------------+

# web (A) と app (A) ならびに web (B) と app (B) はそれぞれ同一インスタンス
これの構成だと致命的な問題としてアプリケーションサーバの根っこの設定を変えたいとかで、ホットデプロイ不可能な再起動したい時に balancer 側を相殺してバランシングからはずさないといけないときに面倒いのです。最初から考慮した運用してれば大丈夫なのかもですが。

まとめ

むりやりまとめると構成Dがいちばん安定するという結論に今なってます。

メリットとしては

  • balancer が特殊なハードやソフトウェアで構成されてる時でも、ゆるふわな人がある程度柔軟な運用が出来る
  • ゆるふわな人がアプリケーションサーバのメンテを気楽にできる
  • アプリケーションサーバが死んでも問題起きないので可用性あがる
  • 古き良きアプリケーションサーバ使ってるときの deploy がある程度ゆるふわに出来る
デメリットとしては
  • static contents が無い場合に、一個余分なサーバを挟む事になるのでパフォーマンスが下がる
  • うっかり web サーバ側のバランシング設定書き忘れて悲しくなる
な感じだとおもいます。

ぼくみたいなしろうとが書いてると机飛んで来そうでこわいけどがんばってかきました><

Posted by Yappo at 19:34 | Comments (0) | TrackBack (0) | tech

2013年05月23日

use strict;
use warnings;
use Benchmark 'cmpthese';

cmpthese( 100 => {
wile => sub {
my $i = 0;
while () {
return if $i++ == 1_000_000;
}
},
goto => sub {
my $i = 0;
LOOP:
return if $i++ == 1_000_000;
goto LOOP;
},
});

__END__

# on v5.16.3
Rate goto wile
goto 9.37/s -- -54%
wile 20.4/s 118% --

# on v5.17.2
Rate goto wile
goto 7.22/s -- -54%
wile 15.8/s 118% --

# on v5.18.0
Rate goto wile
goto 9.30/s -- -43%
wile 16.2/s 74% --

Posted by Yappo at 14:37 | Comments (0) | TrackBack (0) | Perl

2013年05月15日

Amon2::Plugin::L10N だと以下のように書けます。
po/zh-hant.po とか po/zh-hans.po とかは用意しとかなくていいです。きもちわるいけど。

my $LANG_RE = qr/\A(?:en|ja|zh-tw|zh-cn)\z/;
__PACKAGE__->load_plugins('L10N' => {
    default_lang          => 'en',
    accept_langs          => [qw/ en ja zh-tw zh-cn zh-hans zh-hant /],
    po_dir                => 'po',
    before_detection_hook => __PACKAGE__->is_development ? sub {
        my ($c) = @_;

        my $param_lang = $c->req->param('lang');
        if ($param_lang && $param_lang =~ $LANG_RE) {
            $c->session->set('lang', $param_lang);
            return $param_lang;
        } elsif (! defined $param_lang) {
            my $session_lang = $c->session->get('lang');
            if ($session_lang && $session_lang =~ $LANG_RE) {
                return $session_lang;
            }
        }
        $c->session->set('lang', '');
        return;
    } : undef,
    after_detection_hook  => sub {
        my($c, $lang) = @_;
        return 'zh-tw' if $lang eq 'zh-hans';
        return 'zh-cn' if $lang eq 'zh-hant';
        return $lang;
    },
});
Posted by Yappo at 20:45 | Comments (0) | TrackBack (0) | Perl

2013年05月09日

Perl で L10N する方法としてはPerl でつくった web サイトを L10N する方法 - tokuhirom's blog.が有名ですが、昨今はうっかりパンケーキとか唐揚げを食べてたらロンドンとかに海外進出しちゃうようなのが当たり前になった現代においては、 blog のコードをコピペするやつは死んどけば良いので、死なないため殺されない為に実用的に CPANize して殺す側に回りました。

https://metacpan.org/release/Amon2-Plugin-L10N/(現在の最新版は v0.1.3 です。)

貴方のプロジェクトのベースクラス、例えば Amazlet.pm などの中に以下のコードを埋め込みます。

__PACKAGE__->load_plugins('L10N' => {
    accept_langs => [qw/ en ja zh-tw zh-cn fr /],
});

その後 xslate の function として以下のような関数を登録するのです。

sub l {
    my $string = shift;
    my @args = map { Text::Xslate::html_escape($_) } @_; # escape arguments
    Text::Xslate::mark_raw( Amazlet-> context-> loc($string, @args) );
}

そうすると temp/index.tt などの中に [% l('hello! amazlet world!') %] などと書けるようになります。

po/ja.po とかが適切な感じで入っていれば 'hello! amazlet world! が適切な言語の物に変換されて表示されます。

po ファイル管理だるい

自前で ja.po とか作るのだるいので簡単なスクリプト添付してあります。ただ、スクリプトを動かす為のモジュールは標準でインストールされないので

$ cpanm --width-suggests Amon2::Plugin::L10N
とやって入れてから
$ amon2-xgettext.pl en ja zh-tw zh-cn fr 
ってすれば lib や tmpl 以下の中から適切に探して作ってくれます。

まとめ

Amon2::Plugin::L10N を使ってみんなで国際化して変化でガラパゴスしましょう。詳細は pod 見ると良いです。

Posted by Yappo at 17:38 | Comments (0) | TrackBack (0) | Perl

2013年04月25日

月曜日の悪魔こと yappo です。
今時のグローバルな社会ではみなさん多言語対応の web アプリを書いているとおもいますが、その時に使う Accept-Language を解析してくれるのを書きました。

HTTP::AcceptLanguage

Plack なアプリだったら。

my $lang = HTTP::AcceptLanguage->new($req->header('Accept-Language'))->match(qw/ en fr es ja zh-tw /);
ってするだけで、英語フランス語スペイン語日本語台湾語の中からユーザが利用可能な最適な言語を探して返してくれます。

もし適切な言語が無ければ undef を返すので、あなたがおすすめの言語で返しておくといいでしょう。

I18N::AcceptLanguage っていうのが既にあるんですが、名前空間がわかりづらいのと、ドキュメントがわかりづらいのと、コードが余計な事やってるのと、インデントが独自すぎてコード読めないのと、頑張ってコード読むとバグがあったり解釈がRFCの通りになってなかったりして、ハマった時のデバッグが不能だったので作りました。

まぁ、 RFC の件はあんまどうでもいい。っていうか RFC なんか信じてたら世界が破滅するので厳密な実装にはなってなくて、あるていど実用的にしてあります。

Posted by Yappo at 17:06 | Comments (0) | TrackBack (0) | Perl

2013年04月01日

こんにちわ!
先週金曜日に NHN で開催された PerlCasual っていうイベントで Perl の例外処理最新ベストプラクティスの話をして来ました。

http://yappo.github.com/talks/20130329-perlcasual7-exception/

yusukebe が開催するよって言うから、話したいって言ったら「もうスピーカーきめちゃった!」とか言われてたんですが、カジュアルに仲間に入れてもらえました。

スピリチュアルな話とかぼうよくわからないんで感想とか書けないんですが、yusukebeさん駄目な人間さん、NHNさんありがとうございました!

Posted by Yappo at 18:17 | Comments (0) | TrackBack (0) | Perl

2013年03月26日

最近 Perlの話題を日本語で – Lingr という革新性力が活発ですが、やっぱり lingr は情報密度が薄いから、情報密度が濃い irc client から使いたいっていう事で1週遅れで導入しました。

材料

  • Perl(筆者はCentOSに最初から入ってる5.8.8を使っています)
  • Carton(適当に入れてください、筆者は sudo cpan Carton で入れました)
  • git

入れ方

  1. git clone git://github.com/tokuhirom/lingr-ircd.git
  2. cd lingr-ircd (5.8.8 標準の Scalar::Util が Mouse で動かんから cpanfile に requires 'Scalar::Util' => '1.27'; っての追加した)
  3. carton install
  4. Developer CenterのYour Appsのregister a new appで適当にアプリ登録
  5. carton exec -- perl ./bin/lingr-ircd --ircd_host=127.0.0.1 --ircd_port=[好きな奴] --lingr_user=[lingr のユーザ名] --lingr_password=[lingr のパスワード] --lingr_api_key=[Your Apps の key 列]
  6. 移動すると入ってるチャンネル名一覧が出るから、起動した irc server につないで /join #perl_jp とかすればいい
  7. なんかユーザ一覧取れないけど、待ってればだれか発言するし、挨拶でもすれば帰ってくるから接続テスト完了する

happy lingr life !

Posted by Yappo at 15:48 | Comments (0) | TrackBack (0) | Perl