Mojolicious+Redisでチャット
を作った
Mishima.pm #1 @dokechin
Mojolicious
• Perl のWeb Application Framework
• 広大なMojoネーム空間のモジュール群をWeb
アプリで使うようにしたもの
http://blog2.jamadam.com/?p=808 より引用
私の考えるメリット
• 開発コミュニティが世界的に活発
(MojoConf2014 in オスロなど。2015への
積立金もすでにプールされている。)
• ドキュメント豊富、サンプルソースも一杯あふ
れている
• All in One
• 環境構築が楽、cpanm Mojolicious
非同期な話
1.MojoliciousとNode.jsは非同期サーバ
Mojoliciousでsleepしてみた
#!/usr/bin/env perl
use Mojolicious::Lite;
get '/' => sub {
my $self = shift;
sleep(10);
$self->render('index');
};
get '/hoge' => sub {
my $self = shift;
$self->render('hoge');
};
app->start;
/をGETその後すぐ、/hogeをGET
/hogeが先に返ってくるのでは・・
と期待していたが・・・
結果は・・
/が返ってから/hogeが返ってくる!
処理の流れを1つにすることで、サーバー資源を
節約?どこかで聞いたことあるなぁ・・・
非同期な話
2.非同期vs同期ベンチマーク
同期なコード
package Test::Web::Example;
use Mojo::Base 'Mojolicious::Controller';
use Time::HiRes qw(sleep);
sub welcome {
my $self = shift;
sleep(0.5);
$self->render(
message => 'Welcome to the Mojolicious
real-time web framework!');
}
1;
非同期なコード
package Test::Web::Example;
use Mojo::Base 'Mojolicious::Controller';
use Time::HiRes qw(sleep);
sub welcome {
my $self = shift;
$self->render_later;
Mojo::IOLoop->timer(0.5 => sub {
$self->render(
message => 'Welcome to the
Mojolicious real-time web framework!');
});
}
1;
Apache bench
ab –c 100–n 1000 http://your_host_name/
サーバ構成
さくらVPS
メモリ 1G
CPU 仮想2コア
nginx1.6+hyponotad 5workers
nginx1.6+starman 5workers
結果
0
100
200
300
400
500
600
0 0.2 0.4 0.6 0.8 1 1.2
同期(starman+5workers)
非同期(hyponotad+5worker)
同期(hyponotad+5worker)
トレードオフ
容易な同期的プログラ
ミング
難しい非同期プログラ
ミング
サーバリソース大 サーバリソース小
その他、ベンチをやって気づいたこと
・starman,startletでは、Mojoliciousの非同期機能
が使えない
非同期な話
3.実践的な非同期コードについて
同期的コード
sub mojo4{
my $self = shift;
my $ua = LWP::UserAgent->new;
my $res = $ua-
>get("http://atndfc.dokechin.com");
if ($res->is_success) {
$self->render(message => $res->content);
}
else {
die $res->status_line;
}
}
非同期的コード
package Test::Web::Example;
#use Mojo::UserAgent;
sub mojo{
my $self = shift;
$self->ua-
>get('http://metacpan.org/search?q=mojo'
=>sub{
my ($ua, $tx) = @_;
$self->render(message => $tx->res);
});
}
1;
Mojolicious用各種非同期APIを使う
Mojo::Redis(Redis非同期クライアント)
Mango(Mongo非同期クライアント)
ブロッキングAPIしかない場合
Mojo::IOLoop::ForkCall - run blocking functions
asynchronously by forking
非同期な話
3.Callback hellを避けるためには
# callback hell code
sub mojo{
my $self = shift;
$self->render_later;
$ua->get('http://yahoo.co.jp/' =>
sub{ my ($ua, $tx) = @_;
my $title1 = $tx->res->dom->html->head-
>title->text;
$ua->get('http://google.com/' =>
sub{ my ($ua, $tx) = @_;
my $title2 = $tx->res->dom->html->head-
>title->text;
$self->render(msg => $title1 . $title2);
});
});
}
# finish
sub mojo2{
my $self = shift;
$self->render_later;
my $delay = Mojo::IOLoop::Delay->new;
$delay->on(finish=>sub{
my $delay = shift;
my @titles = map { $_->res->dom-
>at('title')->text } @_;
$self->render(message => "@titles");
});
$self->ua->get( $_ => $delay->begin )
for@urls;
}
# stepsを利用したコード
sub mojo3{
my $self = shift;
$self->render_later;
my $delay = Mojo::IOLoop::Delay->new;
$delay->steps(sub{
my ($delay) = @_;
$self->ua->get( "http://www.cpan.org/" =>
$delay->begin);
$self->ua->get( "http://jognavi.com/" => $delay-
>begin);
}, sub {
my ($delay,@args) = @_;
my @messages = map {$_->res->dom->at('title')-
>text} @args;
$self->render(message=> "@messages");
},
);
}
チャットを作った話
• まずはデモ
morbo
W
WebSocket WebSocket WebSocketWebSocket
UserA UserB UserC UserD
・すべておなじプロセス配下のため、同じ部屋
の発言の通知は可能。
・スケーラブルでない
RoomA RoomB
hypnotoad
W
WebSocket WebSocket WebSocketWebSocket
UserA UserB UserC UserD
RoomA RoomB
Wプロセス間通信の手段が必要
Redis(部屋、部屋の入居者管理)
W
WebSocket WebSocket WebSocketWebSocket
W
RoomA RoomB
Red
is
UserA UserB UserC UserD
Redis-オブジェクトキャッシュ
rooms => {“rooma”,”roomb”}
rooma=>{“userA”,”userB”}
roomb=>{“userC”,”userD”}
RedisのPubSub
UserA,UserBがRoomAチャンネルを購買。UserA
がRoomAチャンネルへ発言、チャンネルAの購
買者(UserA,UserB)に発言内容が通知される。
まとめ
• 非同期はパフォーマンスがよくなるが、コード
は複雑化する。
• Mojoliciousは非同期に動かす仕組み(モジュール、
サーバ)が、備わっている。
• 本番環境での運用を視野にするならば、初めか
らRedisなど、Kye-Valueストアを使ったほうが
よい?
ご清聴ありがとうございました

Mojolicious+redisでチャットを作った