mod_mrubyでApacheのスコアボードのデータを取得できるようになりましたので、アクセス数やトラフィック量、及びリクエスト処理中のスレッド数の割合でアクセス制限が可能になりました。
ということで、早速mod_mrubyを使って、Apacheのアクセス制限を行うためのサンプルRubyスクリプトを書いてみました。Githubでもexample/で公開中です。Rubyスキルが低すぎるのでスクリプトに関するツッコミは程々でお願いします…
Apacheの1秒間トラフィック量でアクセス制限
サンプルスクリプトは以下のようになります。
# LoadModule mruby_module modules/mod_mruby.so
# ExtendedStatus On
# mrubyAccessCheckerMiddle /var/www/html/traffic_rate_limit.rb
class Traffic
def initialize(rate)
@rate = rate
end
def kbcheck
sb = Apache::Scoreboard.new()
kbpersec = sb.total_kbyte / sb.uptime * 100
Apache.errlogger(4, "kbpersec: " + kbpersec.to_s)
if kbpersec > @rate
true
else
false
end
end
end
# traffic kbyte / sec is 10000(10MB/sec)
t = Traffic.new(10000)
if t.kbcheck
Apache::return(Apache::HTTP_SERVICE_UNAVAILABLE)
else
Apache::return(Apache::DECLINED)
end
このスクリプトでは、トラフィック量による簡単なアクセス制限を実現するため、Apacheが起動してから処理している毎秒のトラフィック量(kbpersec)が10MB/secを超えた(t.kbcheck)場合に、503を返します。実運用で使う場合には、もう少し凝った実装が必要だと想いますが、これでもそれなりに制限できそうです。
Apacheの設定は、AccessCheckerフェイズでフックさせるようにしておきます。scoreboardの値を得られるようにExtendedStatusもOnにしておきましょう。
LoadModule mruby_module modules/mod_mruby.so
ExtendedStatus On
mrubyAccessCheckerMiddle /var/www/html/traffic_rate_limit.rb
リクエスト処理中のWorkerスレッドの割合や1秒間のアクセス数でアクセス制限
サンプルスクリプトは以下です。
# LoadModule mruby_module modules/mod_mruby.so
# ExtendedStatus On
# mrubyAccessCheckerMiddle /var/www/html/worker_rate_limit.rb
class Worker
def initialize(rate, count)
@sb = Apache::Scoreboard.new()
@myrate = rate
@mycount = count
end
def busycheck
busyrate = @sb.busy_worker / (@sb.server_limit * @sb.thread_limit) * 100
Apache.errlogger(4, "busy worker rate: " + busyrate.to_s)
if busyrate > @myrate
true
else
false
end
end
def accesscheck
accesspsec = @sb.total_access / @sb.uptime
Apache.errlogger(4, "access per sec: " + accesspsec.to_s)
if accesspsec > @mycount
true
else
false
end
end
end
# busy worker thread rate is 90 and access per sec is 1000
w = Worker.new(90, 1000)
if w.busycheck
Apache::return(Apache::HTTP_SERVICE_UNAVAILABLE)
elsif w.accesscheck
Apache::return(Apache::HTTP_SERVICE_UNAVAILABLE)
else
Apache::return(Apache::DECLINED)
end
Apacheで動作しているすべてのWorkerスレッドにおいて、リクエストを処理しているWorkerスレッドの割合(busyrate)が90%を超えた(w.busycheck)場合に503を返します。
また、そのチェック(w.busycheck)は問題なくても、1秒間のアクセス数が1000アクセスを超えた(w.accesscheck)場合に、503を返すような実装もしてみました。
最後に
503を返すような簡単なアクセス制限であれば、このように簡単に実装できるようになってきました。今後は、トラフィックのコントロールもできるようにしたいです。後は、mrubyの拡張ライブラリ(IO、Socket、File、Process)がリリースされれば、もっと色々おもしろい事ができそうなので、リリースが待ち遠しいです。自分で作っちゃってもいいのですが、そこはあまり分散しないように、リリースを待とうとおもいます。