Perl の Coro と RPC::XML::Client モジュールのメモです。
WordPress に画像をアップロードする処理を並列化して高速化しようという試みです。
Coro::LWP を使うと、ちょっとのコード修正だけで LWP を使う部分が並列化で処理できるようになるということを知って、そう言えば RPC::XML::Client も裏で LWP 使ってたなという事を思い出し試してみました。
Coro を使った場合どれ位早くなるかを、同じ画像を 10 回アップロードして測定してみます。
使う画像は下記のダミーイメージファイルです。(ファイルサイズは 293B)
ちょっと長くなるので分割しますが、まずは RPC::XML::Client でファイルをアップロードするサーブルーチンから。
sub wp_upload_file { my ($xmlrpc, $data) = @_; my $method = 'wp.uploadFile'; my $req = ''; my $res = ''; my $fname = ''; my $fmime = ''; my $bits = ''; my $buffer = ''; my ($base, $path, $ext) = fileparse($data, qr/\..+$/); $fname = $base . $ext; $fmime = `$cmd->{file} $data`; open(FILE, "< $data"); binmode FILE; while( read(FILE, $buffer, 1024) ) { $bits .= $buffer; } close(FILE); $req = RPC::XML::request->new( $method, $xmlrpc->{blog}, $xmlrpc->{user}, $xmlrpc->{pass}, RPC::XML::struct->new({ 'name' => RPC::XML::string->new($fname), 'type' => RPC::XML::string->new($fmime), 'bits' => RPC::XML::base64->new($bits), 'overwrite' => RPC::XML::boolean->new(0) }) ); $res = &send_xmldata($xmlrpc->{conn}, $req); if ($res !~ /^fail:/) { $res = $res->value; } return($res); } sub conn_xmlrpc { my $hash = shift; my $method = 'blogger.getUsersBlogs'; my $req = ''; my $res = ''; my $xmlrpc = {}; if (!$hash->{port}) { $hash->{port} = 80; } if (!$hash->{path}) { $hash->{port} = '/xmlrpc.php'; } $hash->{url} =~ s/\/$//; $hash->{server} = $hash->{url} . ':' . $hash->{port} . $hash->{path}; $xmlrpc->{conn} = RPC::XML::Client->new($hash->{server}); $xmlrpc->{user} = RPC::XML::string->new($hash->{user}); $xmlrpc->{pass} = RPC::XML::string->new($hash->{pass}); $xmlrpc->{blog} = 0; $req = RPC::XML::request->new( $method, RPC::XML::string->new(''), $xmlrpc->{user}, $xmlrpc->{pass} ); $res = &send_xmldata($xmlrpc->{conn}, $req); if ($res !~ /^fail:/) { $xmlrpc->{blog} = RPC::XML::string->new(@{$res->value}[0]->{blogid}); } return($xmlrpc); } sub send_xmldata { my ($xmlrpc, $req) = @_; my $res = ''; $res = $xmlrpc->send_request($req); if ($res =~ /error/) { $res = "fail: $res"; } elsif ($res->is_fault) { $res = $utf8->encode($res->value->{faultString}); $res = "fail: $res"; } return($res); }
これを使って、10 回ループ処理でアップロードするコード。
#!/usr/bin/perl use strict; use warnings; my $param = { url => 'http://example.jp/', port => '80', path => '/xmlrpc.php', user => 'admin', pass => 'adminpass' }; my $image = '/path/dummy.png'; my $xmlrpc = &conn_xmlrpc($param); for (my $i=0; $i < 10; $i++) { my $res = &wp_upload_file($xmlrpc, $image); print "$res->{id}\n"; } exit;
実行結果はこんな感じ、7.36sec かかりました。
# time perl xmlrpc_upload.pl 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 ----- 0.451u 0.116s 0:07.36 7.6% 11+6381k 0+0io 0pf+0w
次はこの処理で Coro を使うようにしてみます。
#!/usr/bin/perl use strict; use warnings; use Coro; use Coro::LWP; my $param = { url => 'http://example.jp/', port => '80', path => '/xmlrpc.php', user => 'admin', pass => 'adminpass' }; my $image = '/path/dummy.png'; my $xmlrpc = &conn_xmlrpc($param); my @result = (); for (my $i=0; $i < 10; $i++) { push @result, async { my $res = &wp_upload_file($xmlrpc, $image); print "$res->{id}\n"; }; } $_->join for @result; exit;
実行結果は 3.11sec と倍以上の早さで高速に処理できました。
time perl xmlrpc_coro_upload.pl # time perl xmlrpc_coro.pl 2103 2106 2107 2104 2108 2105 2109 2110 2111 2112 ----- 0.421u 0.144s 0:03.11 18.0% 12+8049k 0+0io 0pf+0w
返ってくる「メディア ID」がバラバラになってるのを見ると並列化されたのがわかります。
お手軽な変更でここまで早くなるなら Coro を使わない手はないですね。今回は同時スレッド数の制限はしませんでしたが、ここは利用するサーバと相談しながら決めた方が良いとは思います。
処理結果は終わった順から返って来るので画像を配置する順番等は別途考慮する必要がありますが、こんな感じでうまくいきました。
簡単ですが、Coro で WordPress のファイルアップロードを高速化は以上です。