work.log

元エンジニアの備忘録的ブログ

PerlのCoroでWordPressのファイルアップロードを高速化

投稿:2014-03-31 18:45  更新:

Perl の Coro と RPC::XML::Client モジュールのメモです。

WordPress に画像をアップロードする処理を並列化して高速化しようという試みです。

Coro::LWP を使うと、ちょっとのコード修正だけで LWP を使う部分が並列化で処理できるようになるということを知って、そう言えば RPC::XML::Client も裏で LWP 使ってたなという事を思い出し試してみました。

Coro を使った場合どれ位早くなるかを、同じ画像を 10 回アップロードして測定してみます。

使う画像は下記のダミーイメージファイルです。(ファイルサイズは 293B)

dummyimage

ちょっと長くなるので分割しますが、まずは 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 のファイルアップロードを高速化は以上です。