MT4i 3.1がGateway Time-outするので弄ってみた。

WS2013-06-06_19_37_53
うちのブログには携帯電話用にMT4iという「MovableType用 携帯電話向け変換プログラム」を設置させていただいています。

MT4i – t2o2-Wiki

スマホ用の対応は以下のプラグインにて行なってみたのですが、ダイナミックパブリッシングのせいなのか、見事に不具合がでたので元に戻しました。残念。

MTにプラグインを導入してスマートフォンに対応してみました。(MT5でもいけた): ソウマクリップ

で、現在MT4i 3.1を利用させていただいていますが、最近Google botとMSN botが頻繁に来ていて、うざいことになっているのですが、

WS2013-06-06_21_09_26

最初の画像の様に、Gateway Time-outしてしまっていて、プロセスを覗いてみるとそのままプロセスがゾンビ化してしまっています。

こまったなーということで、robots.txtでbotを拒否しまくったのですが、googleどんも、MSNどんもまったく来なくなる気配がありません。

しょうがないので、MT4iの方でタイムアウトする原因を調べて見ることにしました。

そして、調査の結果原因は

mt4i2/lib/mt4i/Enc.pl の中にあるsubstrb_sjisというサブルーチンに有ることが分かりました。

このサブルーチンは渡ってきたテキストに対して、指定した位置から指定した長さを取得するいわゆるsubstr関数なわけですが、携帯対応ということがあって色々と複雑な処理をしています。単純に文字数で切り取ることが出来ないので、色々やっているわけです。

切り出すバイト数より実際に切り出した文字数のバイト数が大きい場合、切り出す文字を1文字減らして、またバイト数をチェックみたいなことをやっているわけです。

文字数が少ない記事はいいのですが、文字数がとても多い記事でページ分割が数ページになる場合、ページが進むほどこの誤差が大きくなっていきます。下の数値は実際にデバッグ様に出した数値なのですが、誤差が7000くらいになっています。

tmp_len=32527 start=25810

これを、1文字減らしてバイト数測って比較を繰り返し、tmp_lenの値がstartより小さくなるまで繰り返すという処理を行なっているわけですが、これだといつまでたっても終わらないわけです。

##################################################
# Sub Substrb_sjis
#  Substr for utf-8 as shift_jis
##################################################
sub substrb_sjis {
    my ($str, $start, $length) = @_;

    $start = 0 if $start <= 0;
    if ($start) {
        my $tmp_start = $start;
        my $tmp_str = substr($str, 0, $tmp_start);
        my $tmp_len = lenb_sjis($tmp_str);
        while ($tmp_len > $start) {
            $tmp_start–;
            $tmp_str = substr($str, 0, $tmp_start);
            $tmp_len = lenb_sjis($tmp_str);
        }
        $start = length($tmp_str);
    }

    my $tmp_length = $length;
    my $tmp_str = substr($str, $start, $tmp_length);
    my $tmp_len = lenb_sjis($tmp_str);
    while ($tmp_len > $length) {
        $tmp_length–;
        $tmp_str = substr($str, $start, $tmp_length);
        $tmp_len = lenb_sjis($tmp_str);
    }

    return $tmp_str;
}

元のソースはこうなっています。

##################################################
# Sub Substrb_sjis
#  Substr for utf-8 as shift_jis
##################################################
sub substrb_sjis {
    my ($str, $start, $length) = @_;

    my $skip = 500;

    $start = 0 if $start <= 0;
    if ($start) {
        my $tmp_start = $start;
        my $tmp_str = substr($str, 0, $tmp_start);
        my $tmp_len = lenb_sjis($tmp_str);
        while ($tmp_len > $start) {
#           $tmp_start–;
            $tmp_start-=$skip;
            $tmp_str = substr($str, 0, $tmp_start);
            $tmp_len = lenb_sjis($tmp_str);
        }
        $tmp_start+=$skip;
        $tmp_len = $start+1;
        while ($tmp_len > $start) {
            $tmp_str = substr($str, 0, –$tmp_start);
            $tmp_len = lenb_sjis($tmp_str);
        }
        $start = length($tmp_str);
    }

    my $tmp_length = $length;
    my $tmp_str = substr($str, $start, $tmp_length);
    my $tmp_len = lenb_sjis($tmp_str);
    while ($tmp_len > $length) {
        $tmp_length -= $skip;
        $tmp_str = substr($str, $start, $tmp_length);
        $tmp_len = lenb_sjis($tmp_str);
    }
    $tmp_length += $skip;
    $tmp_len = $length + 1;
    while ($tmp_len > $length) {
        $tmp_str = substr($str, $start, –$tmp_length);
        $tmp_len = lenb_sjis($tmp_str);
    }
    return $tmp_str;
}

とりあえずやっつけでこんなふうに改造してみました。

原理としては、1ずつデクリメントしていくのではきりがないので、500ずつ引くようにしました。ただこれだけだとおかしくなります。

なので、tmp_lenがstartより小さくなった時点で、一度500足し戻して、そこから1ずつデクリメントするという処理にしています。

これで、500ずつスキップする分処理が早く終わるという算段です。

WS2013-06-06_21_18_55
多少表示に時間は掛かるものの、一応、目論見どおりにタイムアウトせずに表示されるようになりました。ページが9ページにも別れるような長文のせいなんですけどね。

もっと効率のいい処理が出来そうな気もしますが、今のところはこれで良しとします。

FireShot Screen Capture #325 - 'KUMA TYPE mobile ver_' - blog_kumacchi_com_mt4i2_mt4i_cgi_page=7


追記:2013.06.10

前よりもましになったものの、まだ、mt4i.cgiがゾンビ化することが有るようで、

WS2013-06-10_16_37_52
グーグル先生にも怒られているし、更に調べて見ることにした。

WS2013-06-10_16_36_05
調べてみると、36ページにも分かれているページでした。mt4iの設定で、ページ記事分割のバイト数を、時代も時代だしデフォルトの4096から8192と倍にして対処してみましたが、どうやらISHのソースを張り付けているせいなのか、制限バイト数より小さなバイト数で改ページになっているでした。なので、設定では回避できなさそうなので、ソースを更に改造することにしました。

もともと、後ろから1バイトずつちまちまチェックしていたのを、500バイトずつスキップしてチェックするようにしていたのですが、結局最後は500バイト戻して500回チェックが入るのですごく効率が悪いわけです。

もっと、ピンポイントでチェックするように出来ればと思いました。

Enc.pl

##################################################
# Sub Substrb_sjis
#  Length for utf-8 as shift_jis
##################################################
sub lenb_sjis {
#    my $str = shift;
#    my $lenb_utf8  = length(Encode::encode_utf8($str));
#       $lenb_utf8 -= $str =~ s/([\x{0800}-\x{FFFF}])/$1/g;
#       $lenb_utf8 -= $str =~ s/([$hankakukana])/$1/g;
#    return $lenb_utf8;

    my $lenb_utf8  = length(Encode::encode_utf8($_[0]));
       $lenb_utf8 -= @{[$_[0] =~ m/([\x{0800}-\x{FFFF}])/g]};
       $lenb_utf8 -= @{[$_[0] =~ m/([$hankakukana])/g]};
    $lenb_utf8;
}

とりあえず、長さチェックのサブルーチンを上のように改造。置換よりマッチングの方が多少は早いのではないかという目論見と、値渡しを参照渡しにしてその分早くなるんじゃないないかという目論見です。効果は薄かったです。時間の違いは計測してないです。

Enc.pl

##################################################
# Sub Substrb_sjis
#  Substr for utf-8 as shift_jis
##################################################
sub substrb_sjis {
    my ($str, $start, $length) = @_;

#    return substr($str, $start, $length);

MT4i::Log::writelog("substrb_sjis begin start=$start length=$length");

    $length = 0 if($length<0);
    $start  = 0 if($start<0);

    my $skip = 500;

    $start = 0 if $start <= 0;
    if ($start) {
        my $tmp_start = $start;
        my $tmp_str = substr($str, 0, $tmp_start);
        my $tmp_len = lenb_sjis($tmp_str);
MT4i::Log::writelog("substrb_sjis 1_1 tmp_len=$tmp_len start=$start");

        my $bk_tmp_len = $tmp_len;
        $skip = int(($tmp_len – $start)/2) || 1;
        MT4i::Log::writelog("substrb_sjis 1_2 tmp_len=$tmp_len start=$start skip=$skip");
        while ($tmp_len > $start) {
            $tmp_start -= $skip;
            $tmp_str = substr($str, 0, $tmp_start);
            $bk_tmp_len = $tmp_len;
            $tmp_len = lenb_sjis($tmp_str);
        }

        $tmp_start+=$skip;
        $tmp_len = $bk_tmp_len;
        $skip = int(($tmp_len – $start)/4) || 1;
        MT4i::Log::writelog("substrb_sjis 1_3 tmp_len=$tmp_len start=$start skip=$skip");
        while ($tmp_len > $start) {
            $tmp_start -= $skip;
            $tmp_str = substr($str, 0, $tmp_start);
            $bk_tmp_len = $tmp_len;
            $tmp_len = lenb_sjis($tmp_str);
        }

        $tmp_start+=$skip;
        $tmp_len = $bk_tmp_len;
        $skip = int(($tmp_len – $start)/8) || 1;
        MT4i::Log::writelog("substrb_sjis 1_4 tmp_len=$tmp_len start=$start skip=$skip");
        while ($tmp_len > $start) {
            $tmp_start -= $skip;
            $tmp_str = substr($str, 0, $tmp_start);
            $bk_tmp_len = $tmp_len;
            $tmp_len = lenb_sjis($tmp_str);
        }

        $tmp_start += $skip;
        $tmp_len = $bk_tmp_len;
        MT4i::Log::writelog("substrb_sjis 1_5 tmp_len=$tmp_len start=$start skip=$skip");
        while ($tmp_len > $start) {
            $tmp_str = substr($str, 0, –$tmp_start);
            $tmp_len = lenb_sjis($tmp_str);
        }
        $start = length($tmp_str);
    }

MT4i::Log::writelog("substrb_sjis 2 start=$start");

    my $tmp_length = $length;
    my $tmp_str = substr($str, $start, $tmp_length);
    my $tmp_len = lenb_sjis($tmp_str);
MT4i::Log::writelog("substrb_sjis 2_1");

    my $bk_tmp_len = $tmp_len;
    $skip = int(($tmp_len – $length)/2) || 1;
    MT4i::Log::writelog("substrb_sjis 2_2 tmp_len=$tmp_len length=$length skip=$skip");
    while ($tmp_len > $length) {
        $tmp_length -= $skip;
        $tmp_str = substr($str, $start, $tmp_length);
        $bk_tmp_len = $tmp_len;
        $tmp_len = lenb_sjis($tmp_str);
    }

    $bk_tmp_len = $tmp_len;
    $skip = int(($tmp_len – $length)/4) || 1;
    MT4i::Log::writelog("substrb_sjis 2_3 tmp_len=$tmp_len length=$length skip=$skip");
    while ($tmp_len > $length) {
        $tmp_length -= $skip;
        $tmp_str = substr($str, $start, $tmp_length);
        $bk_tmp_len = $tmp_len;
        $tmp_len = lenb_sjis($tmp_str);
    }

    $bk_tmp_len = $tmp_len;
    $skip = int(($tmp_len – $length)/8) || 1;
    MT4i::Log::writelog("substrb_sjis 2_3 tmp_len=$tmp_len length=$length skip=$skip");
    while ($tmp_len > $length) {
        $tmp_length -= $skip;
        $tmp_str = substr($str, $start, $tmp_length);
        $bk_tmp_len = $tmp_len;
        $tmp_len = lenb_sjis($tmp_str);
    }

    $tmp_length += $skip;
    $tmp_len = $bk_tmp_len;
    MT4i::Log::writelog("substrb_sjis 2_5 tmp_len=$tmp_len length=$length skip=$skip");
    while ($tmp_len > $length) {
        $tmp_str = substr($str, $start, –$tmp_length);
        $tmp_len = lenb_sjis($tmp_str);
    }
MT4i::Log::writelog("substrb_sjis end");

    return $tmp_str;
}

そして本命のサブルーチンSubstrb_sjisは上の様になりました。

結果、記事表示は前に比べると爆速と言われる領域になりました。前はSubstrb_sjisを1回コールすると数秒~10秒程掛かっていましたが、今は大体1秒以内で返って来ます。なので最大でも記事分割ページ秒数分で表示されるようになったわけです。

処理的には二分探索的なことをやっているわけですが、再帰処理を書こうと思ったらアタマが痛くなったので、ベタで書きました。改良はアタマの良い人に任せます。

これで、グーグル大先生にも怒られないようになるのではないかと思います。

あと、mt4i.cgiに以下のパラメータで異常な値が渡ってくると暴走するようなので、その対処も入れておきました。(botが変なパラメータつけてきて暴走していることがあるので)

sprtpage
sprtbyte

これで、また様子を見てみます。

更に、時代も時代なので、mt4iの設定で分割バイト数は8192にして半角に変換するのもやめてみようかと思います。

タグ : ,