「Tomcat」クラスタ環境を構築する ( セッションレプリケーション有効 )
Tomcat でセッションレプリケーション有効なクラスタ環境を構築してみたので、メモしておきます。
内容的には以下のドキュメントほぼそのままです。ちょっと訳あってバージョンは古いですが 6.0 系でやってます・・・
・Apache Tomcat 6.0 (6.0.43) - Clustering/Session Replication HOW-TO
https://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html
事前に以下のエントリーで書いた内容を実施して、Tomcat を複数インスタンス ( instance1 / instance2 ) 起動できるようにしておく。
・「Tomcat」複数インスタンスで起動する - プログラム日記
http://a4dosanddos.hatenablog.com/entry/2014/03/08/182306
instance1 / instance2 の server.xml にクラスタのための設定を行なう。
※ 以下のコメントアウトの下ぐらいに追記する。
<!-- <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6"> <!-- <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> --> <Manager className="org.apache.catalina.ha.session.BackupManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" mapSendOptions="6"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="5000" selectorTimeout="100" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6"> <!-- <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> --> <Manager className="org.apache.catalina.ha.session.BackupManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" mapSendOptions="6"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="5001" selectorTimeout="100" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
Receiver のポート番号を instance1 / instance2 で異なるようにしています。
Manager には "DeltaManager"、"BackupManager" があるみたいです。前者は all-to-all replication of session ってあるので全ノードに対してレプリケーションする、後者 ( 今回はこっち ) は primary-secondary session replication とあるのでプライマリからセカンダリに対してのみレプリケーションするという感じでしょうか。
In this release of session replication, Tomcat can perform an all-to-all replication of session state using the DeltaManager or perform backup replication to only one node using the BackupManager. The all-to-all replication is an algorithm that is only efficient when the clusters are small. For larger clusters, to use a primary-secondary session replication where the session will only be stored at one backup server simply setup the BackupManager.
設定は上記だけで完了です。以下は確認用のアプリケーション。
セッションを生成するサーブレット、セッションオブジェクトにバインドされた属性を取得するサーブレットで確認してみようと思います。
■ CreateSessionServlet.java
package test; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import domain.Person; import domain.PersonSeri; public class CreateSessionServlet extends HttpServlet { private static final long serialVersionUID = 1L; public CreateSessionServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("personSeri", new PersonSeri("name1")); PrintWriter out = response.getWriter(); out.println("<html><body>"); out.println("Create Session !!"); out.println("</body></html>"); } }
■ GetSessionAttrServlet.java
package test; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import domain.Person; import domain.PersonSeri; public class GetSessionAttrServlet extends HttpServlet { private static final long serialVersionUID = 1L; public GetSessionAttrServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<html><body>"); out.println("PersonSeri " + ((PersonSeri)request.getSession().getAttribute("personSeri")).getName()); out.println("<br>"); out.println(request.getSession().getId()); out.println("</body></html>"); } }
セッションオブジェクトにバインドしている PersonSeri は以下の感じです。Serializable インターフェイスを実装するのを忘れないようにする。
package domain; import java.io.Serializable; public class PersonSeri implements Serializable { private String name; public PersonSeri(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
web.xml に distributable 要素を設定しておく。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> - 略 ( CreateSessionServlet、GetSessionAttrServlet のサーブレットマッピングの設定 ) <distributable /> </web-app>
instance1 と instance2 にそれぞれデプロイする。で、以下の順にブラウザからアクセスする。
1. instance1 の CreateSessionServlet でセッションを生成する
2. instance1 の GetSessionAttrServlet でセッションオブジェクトにバインドされた値を取得する ( ブラウザ上に PersonSeri : name1 と表示される)
3. instance2 の GetSessionAttrServlet でセッションオブジェクトにバインドされた値を取得する
3 の際に instance2 ではセッションを生成していないにもかかわらず、セッションレプリケーションされているので、ブラウザ上に PersonSeri : name1 と表示されるはずです。
以上です。
[ 環境情報 ]
CentOS 6.2
Apache Tomcat 6.0.43
Java SE 6 Update 37