このページでは以下の環境での動作を説明しています。
- Debian Linux Etch
- Apache2.2.3
- Tomcat5.5
- J2SDK 5.0 Update10
Tomcatでクラスタリングを行うことで、セッション情報の共有が行われます。これによって使用中のTomcatがダウンしても、別のTomcatに接続することでセッションを継続することができるはずです。しかし、これは現実的でありません。使用中のTomcatが動作を停止したとき、ユーザーはブラウザのURLを手で書き換えて稼働中のTomcatに明示的につなぎに行く必要があります。しかも、通常セッションIDはcookieに格納されています。これではURLが変更した場合セッションIDが取得できません。つまり、cookieが無効になっていてURLにセッションIDが組み込まれているときに、手作業でURLを書き換えることでしか、セッションを継続できません。そこで、Apacheをロードバランサとして使用することで、この問題を解決します。Apacheを中継することで接続先のURLはApacheのURLのみとなります。障害時にはApacheがTomcatサーバの死活を判断し、動作しているTomcatに処理を割り振ります。
Tomcatのロードバランスには以下のモジュールを使用します。
- mod_proxy
- mod_proxy_ajp
- mod_proxy_balancer
mod_proxyの設定はmod_proxy_ajp を使用して Apache と連携するを参考にしてください。ロードバランサの設定はproxy_ajp.confに記述することにします。
<IfModule mod_proxy_ajp.c>
<IfModule mod_proxy_balancer.c>
<Location /ClusterSample/>
ProxyPass balancer://ClusterSample/ nofailover=Off
</Location>
<Proxy balancer://ClusterSample/>
BalancerMember ajp://192.168.0.1:8009/ClusterSample/ loadfactor=10
BalancerMember ajp://192.168.0.2:8009/ClusterSample/ loadfactor=10
</Proxy>
</IfModule>
</IfModule>
以上の設定でTomcatを動作させると、毎回アクセスするサーバーが変更されます。(JSPに各々サーバ名を記述するなどして、どのサーバを使用して表示されてるいるのかわかるようにしてみてください)しかし、最初に使用したサーバを使用し続け、障害時にのみ別のサーバに切り替わるようにしたい場合もあると思います。その場合はStickySessionを利用することで実現できます。
proxy_ajp.confに設定を追加します。
<IfModule mod_proxy_ajp.c>
<IfModule mod_proxy_balancer.c>
<Location /ClusterSample/>
ProxyPass balancer://ClusterSample/ stickysession=JSESSIONID nofailover=Off
</Location>
<Proxy balancer://ClusterSample/>
BalancerMember ajp://192.168.0.1:8009/ClusterSample/ loadfactor=10 route=jvm1
BalancerMember ajp://192.168.0.2:8009/ClusterSample/ loadfactor=10 route=jvm2
</Proxy>
</IfModule>
</IfModule>
stickysessionにJSESSIONIDを指定していますが、これはURLやcookie内のJSESSIONIDの後にピリオドを付加して、その後ろにrouteの文字列を付加することでサーバを割り振るための設定です。ここで注意すべきなのは、JSESSIONIDにrouteの文字列を付加するのはmod_proxy_balancerの仕事ではないということです。mod_proxy_balancerは文字列を読み取ってサーバを割り振ることだけしかしません。それではどのようにしてJSESSIONIDを加工するのでしょうか。実はTomcatにその設定があります。
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host). -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1">
-->
<!-- Define the top level container in our container hierarchy -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<!-- The request dumper valve dumps useful debugging information about
the request headers and cookies that were received, and the response
headers and cookies that were sent, for all requests received by
this instance of Tomcat. If you care only about requests to a
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host). -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1">
-->
<!-- Define the top level container in our container hierarchy -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm2">
<!-- The request dumper valve dumps useful debugging information about
the request headers and cookies that were received, and the response
headers and cookies that were sent, for all requests received by
this instance of Tomcat. If you care only about requests to a
これで、JSESSIONIDの後ろに.jvm1もしくは.jvm2といった文字列が付加されるようになります。
StickySessionを利用する上で、困った問題があります。上記ではstickysession=JSESSIONIDのように設定していますが、文字列("JSESSIONID")の大文字小文字を区別してしまうのです。これがなぜ問題かというと、Tomcatでは普通、CookieにJSESSIONIDという名前の項目でセッションIDを保存します。しかし、Cookieが無効な場合URLにセッションIDを組み込むのですが、このときはjsessionidと小文字になってしまうのです。大文字のJSESSIONIDと小文字のjsessionid、両方に対応するような設定は現状では記述できませんので、どちらかをあきらめなければなりません。
*1 負け組SEの日常(http://makegumi.jp/blog/2007/06/mod_proxy_balancerstickysessio.html)様で、この問題に対応するパッチが公開されています。