バッチ処理などで、リソース(APIやDBなど)にアクセスする際に次のようなコードを書いたことある人は多いのではないでしょうか。
<?php
$page = 1;
while ( $result = $api->fetch("/some/api?page=" + $page) ) {
$cache->set($result);
if ( $page % 10 === 0 ) {
sleep(1);
}
}
このコードには、(問題にならないことも多いでしょうが)いくつか問題があります。
10回に1回のsleepで大丈夫とは限りません。もしかすると、9回目のアクセスでAPIを提供するサイトが負荷がかかって重くなってしまうかもしれません。
1秒のスリープをしたとしても、ちょうど1秒後のアクセスのタイミングで、他のユーザーの大量のアクセスが集中すれば、結果的にAPIを提供するサイトは重くなってしまいます。
というわけで、ThrottleProcというライブラリを作ってみました。(Composerでもそのうち提供する予定です。)
これを使って書くと次のような感じになります。
<?php
require_once('path/to/lib/src/ThrottleProc.php');
$throttle = new ThrottleProc(0.1);
$page = 1;
while ( true ) {
$throttle->start();
$result = $api->fetch("/some/api?page=" + $page);
if ( $result === null ) {
break;
} else {
$cache->set($result);
$throttle->finishAndSleep();
}
}
もちろんこれで重くならない、というわけではないのですが、cacheにセットする処理に時間がかかったり、apiを取得する処理に時間がかかった場合、それに応じて間隔を自動調整してくれるので、重いリクエストを投げ続けてさらに負荷をかける、というのを緩和できます。
ruby版も前に作りましたが、perlのSub::Throttleを移植したライブラリです。phpだとClosureを受けるという方式だとちょっと都合が悪いことがありそうだったので、呼び出しがstart/finishAndSleepと分かれています。