ソケット経由で非同期にコマンドを実行する
POE習作につき。
まず実行スクリプト
#!/usr/bin/env perl use strict; use warnings; use IO::Socket::INET; my $sock = IO::Socket::INET->new("localhost:12345"); $sock->timeout(10); $sock->say("wait:5"); $sock->say("wait:2"); $sock->shutdown(2); exit(0);
ソケットサーバ
#!/usr/bin/env perl use strict; use warnings; use POE qw/Component::Server::TCP Wheel::Run/; use POE::Sugar::Args; #via http://d.hatena.ne.jp/sfujiwara/20070209/1171010387 #sub Socket::SOMAXCONN { 1024 } POE::Session->create( inline_states => { _start => sub { my $poe = sweet_args; $poe->kernel->sig( CHLD => "got_sigchld" ); my $server = POE::Component::Server::TCP->new( Port => 12345, ClientConnected => \&client_connect_handler, ClientDisconnected => \&client_disconnect_handler, ClientInput => \&client_input_handler, ClientShutdownOnError => 1, ); $poe->heap->{server} = $server; }, got_child_stdout => \&child_stdout_handler, got_sigchld => \&sigchld_handler, } ); POE::Kernel->run; exit(0); sub client_input_handler { my $poe = sweet_args; my ($client_input) = @{ $poe->args }; POE::Wheel::Run->new( Program => './sleep.pl', ProgramArgs => [$client_input], StdoutEvent => 'got_child_stdout', ); print "[POE] input: $client_input\n"; } sub client_connect_handler { my $poe = sweet_args; my $ip = $poe->heap->{remote_ip}; print "[POE] connected from $ip\n"; } sub client_disconnect_handler { my $poe = sweet_args; my $ip = $poe->heap->{remote_ip}; print "[POE] $ip disconnected\n"; } sub child_stdout_handler { my $poe = sweet_args; my ($record) = @{ $poe->args }; print "[POE][STREAM] $record\n"; } sub sigchld_handler { my $child_pid = sweet_args->args->[1]; print "[POE] sigchld: $child_pid\n"; return 0; }
ソケットサーバから叩かれるsleep.pl
#!/usr/bin/env perl use strict; use warnings; use IO::File; my $client_input = shift; if($client_input =~ /^wait:(\d+)/) { my $wait = $1; sleep $wait; } #出力についての理解が浅いため… my $f = IO::File->new('test.txt', 'a'); $f->say($client_input); $f->close; print "[input] $client_input\n"; exit(0);
出力は、まずサーバ側
[POE] connected from 127.0.0.1
[POE] input: wait:5
[POE] input: wait:2
[POE] 127.0.0.1 disconnected
[POE] sigchld: 12959
[POE] sigchld: 12958
「wait:5」の方が先に入力されてる。sleep.plでのファイルへの出力は
wait:2
wait:5
期待通り、「wait:2」の方が先に処理が終わってる。
ただ、よくありがちっぽい問題なので、既に誰かがもっといい解決法を見つけてるのかも…。
個人的には、POE::Wheel::Runではなく(つまりコマンドで呼び出すのではなく)、ソケットサーバ内に処理を書くなりUSEできて、実行関数をP::W::Run->newのところで叩けれるようになれば、実行時間がロード時間分短縮されて、メモリにも優しいかな、と思ってる。とりあえず、それは(今の僕だと)一筋縄にいかないことだけは分かった。