41
YAPC::Asia 2009 Tokyo 2009/9/10 宮宮 宮宮

Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

YAPC::Asia 2009 Tokyo

2009/9/10

宮下 剛輔

Page 2: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

自己紹介 宮下 剛輔( mizzy ) ペパボで技術責任者というのをやってま

す Perl 界一の子だくさん?

11 月に 4 人目が生まれる予定です

Page 3: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 4: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 5: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

アジェンダ イベント駆動型プログラミングによる非

同期処理 Danga::Socket の非同期処理の仕組み Perlbal の動作概要 Perlbal プラグインでの非同期処理 まとめ

Page 6: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 7: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

イベント駆動型プログラミング イベントを待機し、起こったイベントに

従って処理を行うプログラミングパラダイム

フロー型プログラミングに対する概念

Page 8: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

イベントの種類 I/O

I/O の読込可能 / 書込可能状態でイベント発生

タイマー一定時間後にイベント発生

シグナルシグナル受信時にイベント発生

子プロセス子プロセス終了時にイベント発生

Page 9: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

メインループ イベント駆動型プログラミングは、「イベ

ント処理登録」 → 「メインループ突入」という流れ

イベントループとも呼ばれる イベント発生の有無をループする度に確認

し、発生したイベントに応じた処理を行う イベント処理後ループに戻り、次にイベン

ト発生するまでループを繰り返す このような流れで非同期に処理を行う

Page 10: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

メインループの例

タイマー処理

I/O イベント待ち

I/O イベント処理

ポストメインループ処理

Page 11: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 12: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Danga::Socket が対応してるイベント

I/O イベントタイマーイベント

Page 13: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

I/O イベント処理 Danga::Socket では以下の I/O 多重化機

構に対応しているkqueueepollpoll

適切な I/O 多重化機構を自動で選択してくれる

Page 14: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

epoll の例use Sys::Syscall$epfd = epoll_create(1024);epoll_ctl($epfd, EPOLL_CTL_ADD, $fd, EPOLLIN|EPOLLOUT);

while(1) { # ここからイベントループ epoll_wait($epfd, 1024, $timeout, \@events);

for $event ( @events ) { # イベントに応じた処理 }}

Page 15: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Danga::Socket での I/O イベント登録use base ‘Danga::Socket’;

sub new { # ファイルディスクリプタを newに渡す $self->SUPER::new($fd); }

sub event_read { # $fdが読込可能になった時に実行}

sub event_write { # $fdが書込可能になった時に実行}

Page 16: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Danga::Socket での I/O イベント登録 その 2

Danga::Socket->AddOtherFds( $fd => sub { # read イベントでの処理 },);

Page 17: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Danga::Socket での I/O イベント登録 その 3

Danga::Socket::Callback->new( handle => $fd, on_read_ready => sub { # readイベント処理 }, on_write_ready => sub { # writeイベント処理 },);

Page 18: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Danga::Socket でのタイマーイベント登録

Danga::Socket->AddTimer( 10, sub { # 10秒後に実行される処理 },}

Page 19: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Danga::Socket でのメインループの開始

Danga::Socket->EventLoop();

Page 20: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

メインループ処理のおさらい

タイマー処理

I/O イベント待ち

I/O イベント処理

ポストイメインループ処理

Page 21: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 22: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Perlbal の動作(リバースプロキシとして利用の場合)

BackendHTTP

ClientProxy

TCPListenerClient

Server

ClientProxy

Client

BackendHTTP

Danga::Socket ベースのオブジェクト

Page 23: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

Perlbal プラグインの動作(start_proxy_request フック)

BackendHTTP

ClientProxy

TCPListenerClient

Server

Plugin::Hoge

今回のターゲット

Page 24: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 25: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

対象となるフック start_proxy_request フックでの実行を

前提として解説していきます

Page 26: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

同期処理プラグインの場合

ClientProxy

TCPListenerClient

Server

Plugin::SyncClient

Page 27: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

非同期処理プラグインの場合

ClientProxy

TCPListenerClient

Server

Plugin::AsyncClient

ClientProxy

Page 28: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

プラグインを非同期にするには? プラグインの処理を Danga::Socket の

イベントループに組み込むDanga::Socket ベースのクラスを作成または Danga::Socket::Callback を利用して処理を書く

Danga::Socket::AddTimer でタイマー処理しても OK

利用するライブラリにも注意○ ブロックしないライブラリを使う○ どうしてもブロックしてしまう処理は、 Gearman::Client::Async で外出し

Page 29: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

プラグインを非同期にするには? 非同期処理が完了したら、次のフェーズ

に処理を移すプラグインのコールバックを利用Perlbal 本体の修正も必要

Page 30: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

非同期処理するクラスpackage My::Drizzle;

use base ‘Danga::Socket’;use Net::Drizzle ':constants';

sub new { $self->SUPER::new($fh); $self->watch_read(1);}

sub event_read { # DBリクエスト状態確認と完了時のコールバック処理}

Page 31: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

注意 Danga::Socket::Callback をつかっても

OK AddOtherFds は使えない

AddOtherFds で登録されたファイルディスクリプタは、イベントループ突入の最初にしか EPOLL_CTL_ADD や EV_ADD されない

直接 epoll_ctl とか呼び出してもいいけど、ポータビリティは低い

Page 32: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

プラグイン register 処理package Perlbal::Plugin::AsyncDb;

sub register { my ( $class, $svc ) = @_;

$svc->register_hook( 'Async' => 'start_proxy_request', \&request_db, );

return 1;}

Page 33: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

非同期処理クラスの呼び出しsub request_db { my Perlbal::ClientProxy $client =

shift;

My::Drizzle->new( callback => sub { # 完了時のコールバック処理 }, );

return 1; # 超重要ポイント!}

Page 34: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

return 0 の場合

BackendHTTP

ClientProxy

TCPListenerClient

Server

Plugin::Async

Plugin::Async の処理が終わらないのに次の処理へ

Page 35: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

BackendHTTP

return 1 &コールバック処理

ClientProxy

TCPListenerClient

Plugin::Async

処理を終了してイベントループへ コールバック

Server

return 1

Page 36: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

コールバックで必要な処理 ClientProxy の処理が handle_request() の途

中で終了してイベントループに戻っている プラグインの処理が完了した

ら、 ClientProxy の次の処理から再開 停止してるのは、 handle_request() 中の以

下のコード return if $svc->run_hook( 'start_proxy_request', $self ); この次から処理を再開するようにする

Page 37: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

コールバック処理my Perlbal::ClientProxy $client = shift;

My::Drizzle->new( callback => sub { $client->{async_complete} = 1;

$client->handle_request; },);

Page 38: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

ClientProxy::handle_requestの修正- return if $svc->run_hook(- 'start_proxy_request', $self- );+ unless ( $self->{async_complete} ) {

+ return if $svc->run_hook(+ 'start_proxy_request', $self

+ );+ }+ $self->{async_complete} = 0;

Page 39: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法
Page 40: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法

非同期処理プラグインを書くポイント 非同期にしたい処理は Danga::Socket のイ

ベントループにつっこむ プラグイン処理が呼び出されたら return 1 プラグイン処理が完了したら、コールバッ

クで処理を再開してやる Perlbal 本体の修正も必要 プラグインの実行順にも注意

return 1 するとそれ以降の同じフックのプラグインは実行されない

Page 41: Danga::Socketの非同期処理の仕組みとPerlbalで非同期処理するプラグインを書く方法