ウェブアプリケーションで必ず5秒以内に応答を返す方法
ソーシャルアプリのアプリケーションサーバーは5秒とか10秒以内にリクエストを返さないとプラットフォームの中継サーバーからタイムアウト扱いにされる。
で、一定時間内に一定数以上のタイムアウトが起こると新規ユーザー登録停止などのペナルティをプラットフォームから受けるようになっている。
これはとても痛い。
なので必ず規定時間内に応答を返したいのだがどうしたらよいのか。
答えとしてはロードバランサーを利用して実現することができる。
要はバックエンドが一定時間内に応答を返してこなかった場合にロードバランサーが代わりに応答を返すというやり方。
ロードバランサー自身が5秒以内に応答を返せないくらいにいっぱいいっぱいになる可能性もあるが、そんなことは滅多にないだろう。
Apacheのmod_proxy_balancerによる設定
Apacheのmod_proxy_balancerでバックエンドから応答が返ってくるまでのタイムアウトを設定できる。
これによりクライアントがタイムアウトする前に「502 ProxyError」を返すことができる。
加えてErrorDocumentを適切に設定すればHTTP的には全くエラーが起きてないように振る舞うことも可能。
例えば以下のように設定すればバックエンドサーバーから4秒以内に応答が返ってこなかった場合にhttp://example.com/sorry.htmlへとリダイレクトされる。
ProxyPass /example http://backend.example.com timeout=4
ErrorDocument 502 http://example.com/sorry.html
このときクライアントへの応答は302のリダイレクトになり、http://example.com/sorry.htmlを表示した時点で200となる。
注意すべきこと
これでプラットフォームから課せられるペナルティの恐れはなくなったが、一方でエラー画面ばかり表示されるアプリに大してユーザーがどういう感想を抱き、どういう行動を取るかは明らか。
またタイムアウトが起きているという事は把握しておかないとその状況を改善するための適切な対処がとれなくなるということも忘れてはならない。
ご利用は計画的に。
ダメだった代替案
もっと簡単にできないものかとちょっと調べたり試した。
ApacheのTimeout
このディレクティブが扱うのはクライアントとの通信のタイムアウトであって、内部処理がどれくらいかかったからタイムアウトさせるというものではなかった。
Apache10年以上触ってるのに初めて知った衝撃の事実。
PHPのmax_execution_timeあるいはset_time_limit()
これで設定できるのはスクリプト自体の実行時間の制限であって応答時間ではない、とのことだ。
つまりスクリプト自身が外部のリソースにアクセスして応答を待っている時間などは制限を受けない。
例えばスクリプトからデータベースを叩いて応答を待っている時間などはスクリプトの実行時間ではない。
こういう事情により以下のコードはタイムアウトしない。
< ?php
set_time_limit(5);
sleep(10); // 寝ている時間は実行時間に含まれない
echo 'hoge';
?>