mod_rewriteでサーバーの負荷が高いときだけリダイレクトする

 サイト運営しているWeb サーバがアクセス負荷でぶっとんだので、ロードアベレージを元にmod_rewrite で503 出せないかと調べたら、Google 様がこのサイトを真っ先に教えてくれた。

RewriteMap ldavg prg:/path/to/loadaverage.pl

 なるほどねって感じ。ただ、みんな褒めちぎるばかりだが、このPerl スクリプトには異論あり。

というか、なんでPerl ?

 一瞬を左右する処理をPerl なんかに任せていいの?

  • Perl 起動のオーバーヘッドは?
  • Perl のシステムコマンド(?)呼び出しのオーバーヘッドは?
  • 子プロセスがバンバン起動しない?
Perl 起動のオーバーヘッドは?

 どうやら無視できるようだ。

このプログラムは Apache サーバの起動時に一度だけ起動され、 stdin および stdout ファイルハンドルを通して、書き換えエンジンとのやりとりを行います。 このプログラムは、各々のマップ関数の検索のたびに、 検索対象のキーを、改行文字で終端された文字列として stdin から受け取ります。 そして、値が見つかれば改行文字で終端された文字列を返し、 見つからなければ (すなわち、与えられたキーに対応する値がない)、 4 文字の文字列 ``NULL'' を返さなければなりません。

Perl のシステムコマンド(?)呼び出しのオーバーヘッドは?

 さて問題の箇所。

my ($ldavg1, $ldavg2, $ldavg3) = `uptime` =~ /load average:\s+([.0-9]+),\s+([.0-9]+),\s+([.0-9]+)/;

 正規表現が怪しいのはまあご愛嬌。

 ただPerl の中でシステムコマンドを呼んでいる。perlop に依れば /bin/sh を呼び出すに似るとある。

A string which is (possibly) interpolated and then executed as a system command with /bin/sh or its equivalent. Shell wildcards, pipes, and redirections will be honored. The collected standard output of the command is returned; standard error is unaffected. In scalar context, it comes back as a single (potentially multi-line) string, or undef if the command failed. In list context, returns a list of lines (however you've defined lines with $/ or $INPUT_RECORD_SEPARATOR), or an empty list if the command failed.

 つまりシェル呼び出しによるオーバーヘッドが発生する可能性がある。uptime コマンドもパス指定していないため検索する必要があるので、やはりシェルを呼び出す可能性が高い。

子プロセスがバンバン起動しない?

 バッククオートでのコマンド実行はsystem 関数に似る。

Does exactly the same thing as exec LIST , except that a fork is done first, and the parent process waits for the child process to complete.

Beginning with v5.6.0, Perl will attempt to flush all files opened for output before starting the child process, but this may not be supported on some platforms (see perlport). To be safe, you may need to set $| ($AUTOFLUSH in English) or call the autoflush() method of IO::Handle on any open handles.

 命令のたびに子プロセスを立ち上げてるのではないか。

結論として

 Perl を使うことそのものに問題はないのは分かった。だがスクリプトがあまりよろしくない。

 どうせ子プロセスでシェルを呼び出すんなら、始めっからシェルスクリプトで書けば余計なオーバーヘッドを回避できるのではないか。

 ベンチマークを取って検証する必要がある。