複数のテストサーバをリバースプロキシで集約 (3)

複数のテストサーバをリバースプロキシで集約 (1) - daily dayflower複数のテストサーバをリバースプロキシで集約 (2) - daily dayflower の続きです。

mod_rewrite の RewriteMap を使ってごにょごにょしましたが,なんともまどろっこしかったです。そもそも URI の書き換えに癖のある DSL を使う mod_rewrite を使わなきゃいけないということ自体がアレです。もっと手になじんだプログラミング言語で書ければロジックもすっきりするのに!

というわけでモジュールを書いてみました(mod_proxy_mapper.c - daily dayflower)。

プロキシ専用ですが,サブリクエストを使ってプロキシ先を選定するモジュールです。

  • サブリクエストを使っているので,Apache でサポートしている言語ハンドラ……CGI*1 なり PHP*2 なり mod_perl((mod_perl 使うならこんなの使わずそもそも自分で PerlFixupHandler 書くべきでしょう。)) なりを使ってかける
  • リクエストのたびにサブリクエストを発行するのでパフォーマンスゲインは相当のもの

なんて toy module ですが,まぁ後者は(プロダクション環境でなければ)そんなに気になるほどのことでもないです。

2008-11-18 追記
cons を強調して書いたので一応 pros も書いておくと,

  • 自由自在なアルゴリズムでプロキシ先を決定できる
    • たとえばアクセスした日時によりプロキシ先を決定したり,アクセス元 IP address にしたがってプロキシ先を決定したり

2008-11-18 追記おわり

LoadModule proxy_module        modules/mod_proxy.so
LoadModule proxy_http_module   modules/mod_proxy_http.so
LoadModule proxy_mapper_module modules/mod_proxy_mapper.so

ProxyMapper file:///var/www/main/proxymap/mapper.cgi

<Directory /var/www/main/proxymap/>
    Options ExecCGI

    AddHandler cgi-script cgi
#   AddHandler persitentperl-script cgi

    Order allow,deny
    Allow from all

    ProxyMapper none
</Directory>

のように設定します。

ProxyMapper という設定子でプロキシマッピングのためのファイルパス/URI パスを指定します。URI パスも指定できますが,サブリクエストなので外部サーバに投げることはできません。ので Fully Qualified URI ではなくパスを指定してください。そもそもモジュールの性質上,ファイルパス(file:// を先頭に書くとファイルパスとみなします)を指定したほうがよいです。

またトップで ProxyMapper を指定していますが,一応 <Location>.htaccess レベルでも設定できます。が,やはり性格上,トップで指定することになるでしょう。

mapper.cgi の例です。

#!/usr/bin/perl

use strict;
use warnings;

our $CONFIG = <<'END_YAML';
---
outer1.example.com:
  /path1: http://inner-a.example.com/path1
  /path2: http://inner-b.example.com/path2
  /: NULL
outer2.example.com:
  /path3: http://inner-b.example.com/path3
  /: NULL
END_YAML

use YAML;

my $server = $ENV{HTTP_HOST};
my $path   = $ENV{REQUEST_URI};

#my $mapping = YAML::LoadFile('proxy.yaml');
my $mapping = YAML::Load($CONFIG);

my $result;
if (exists $mapping->{$server}) {
    my $len = 0;
    foreach my $key (%{$mapping->{$server}}) {
        next  if $len > length $key;

        if (substr($path, 0, length $key) eq $key) {
            $result = $mapping->{$server}->{$key};
            $result .= substr $path, length $key;

            $len = length $key;
        }
    }
}

if (defined $result) {
    print "Location: ${result}\n\n";
}
else {
    print "Status: 404 Not Found\n\n";
}

Location ヘッダに Fully Qualified URI を指定すると mod_proxy に移譲します。ステータスコードを 404 にすると,元のリクエストのステータスコードも 404 になります。

おわりに

(プロキシ)マッピングをよりロジカルに書きたいというだけなら mod_luaLuaHandlerFixup を書くとかすればよかった気がしてきたー。外部プログラムのためにサブリクエストを発行するのと,組み込まれた Lua を実行するのとどっちがパフォーマンスがいいんだろう。

*1:通常のリクエスト処理がとんでもなく遅くなると思います。

*2:でも PHP って worker MPM サポートしてないんですよね。