技術的雑談-複数のTomcat間でSessionの途切れないClusterを作る
環境
- 使用OS:CentOS 3.5
- httpd:Apache 2.0.46(多分2.0.xだったら大丈夫)
- JDK 1.5.0_10
- Apache tomcat 5.5.17
- mod_jk(tomcat-connector) 1.2.20
- 複数のTomcatを立ち上げてmod_jk経由でロードバランスさせるが完了していること。
目的
- Tomcat5.5の複数のインスタンス間でWeb ApplicationのObjectを共有し、フェイルセーフなClusterを作る。
今回の完成版の構成図は以下のようになります。
┌――――┐ httpリクエスト→ │ Apache ├―――┐ 処理を依頼 ┌――――┐ | |mod_jk|―――――→| Tomcat1| httpレスポンス← | | |←―――――| | | | | 結果を返す └――――┘ | | | ↑ | | | SessionなどのObjectのミラーリング | | | ↓ | | | 処理を依頼 ┌――――┐ | | |―――――→| Tomcat2| | | |←―――――| | | | | 結果を返す └――――┘ └――――┴―――┘ ※ 2つのTomcatは同一OSインスタンス上で稼動
手順
Tomcatが使用するリソース
複数のTomcatを立ち上げてmod_jk経由でロードバランスさせるでのリソースに加え、以下のリソースを必要とします。
- IPマルチキャスト用のIPアドレス1つ、Port1つ
- Clusterへのメンバー追加、削除、状態変更通知、ハートビート(生存確認)などに使われているっぽいです。(すいません、ちゃんとは調べていませんが、多分server.xmlの同じエレメントに「mcastFrequency="500"」とか「mcastDropTime="3000"」とかあるので、そうかなぁ…と。)Defaultのserver.xml(コメントアウトされている)では「228.0.0.4」「45564」が使われています。
- TCP Socket 4001番
- 実際のTomcat Objectの複製の為に使われるようです。
ここでの「マルチキャスト用IPアドレス・ポート」はClusterを組むTomcatのserver.xmlで全部同じにするのが正しいようです。
(IPアドレスにみんなで同じものを設定するなんて…理解しがたいですがこういうものらしいです。)
一方、Port 4001番の方は同一OSインスタンス上に複数のTomcatがいる場合、ずらしておかないとダメなようです。
(多分、マルチキャストで送っている情報の中に「私が変更を受け付けているのは4001番ポートだよ〜」みたいな情報が載っているんでしょうね。)
準備
Clusterの接続確認・生存確認にIPマルチキャストを使っているので、TomcatどうしがRouterの向こうにいる場合、そのRouterはIPマルチキャスト対応のRouterでなくてはならないようです。
多分、よくわからないうちや技術検証中はHub(RouterではなくSwitch)経由で接続する方が良いのではないかと思われます。
また、ここでは特に試していませんが、マルチキャスト用のNICと複製用のNICは別にできるっぽいです。
Install
Tomcatを2台以上と、それをちゃんと振り分けられるようにmod_jk、Apache httpdをセットアップしておいてください。
Apache httpd + mod_jk + Tomcat5.5の環境が構築できていればその他にソフトウェアを追加インストールする必要はありません。
設定
まずは、全Tomcatのserver.xmlを編集します。
Defaultのserver.xmlを使用している場合、後半部分はClusterの設定がコメントアウトされた状態で記載されています。
今回、私がCluster用に書き加えたのは以下のものです。
<Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster" managerClassName="org.apache.catalina.cluster.session.DeltaManager" expireSessionsOnShutdown="false" useDirtyFlag="true" notifyListenersOnReplication="true"> <Membership className="org.apache.catalina.cluster.mcast.McastService" mcastAddr="228.0.0.4" mcastPort="45564" mcastFrequency="500" mcastDropTime="3000"/> <Receiver className="org.apache.catalina.cluster.tcp.ReplicationListener" tcpListenAddress="auto" tcpListenPort="4001" tcpSelectorTimeout="100" tcpThreadCount="6"/> <Sender className="org.apache.catalina.cluster.tcp.ReplicationTransmitter" replicationMode="synchronous" waitForAck="true" /> <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/> <Valve className="org.apache.catalina.cluster.session.JvmRouteBinderValve" enabled="true" /> <ClusterListener className="org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener" /> <Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/> </Cluster>
これを「</Host>タグ」の直前に追記します。
また、Clusterに参加する全Tomcatのserver.xmlに同じように追記します。
ただし、同一OSインスタンス上に複数のTomcatが稼動している場合のみ、<Receiver>内の「tcpListenPort」の値を、ずらしてください。<Membership>の「mcastAddr」や「mcastPort」は変える必要が ありません。 むしろ変えちゃいけないっぽい。
- <Cluster useDirtyFlag="true" >
- Web Application内でTapestryを使っている場合はこれをfalseに変えないと不具合がおこるらしいです。(未確認)
- <Membership>
- 同じClusterグループに属しているサーバを見つけ出す為に使われるっぽいですね。(未確認)NICが複数刺さっているサーバの場合、228.0.0.4宛てのパケットが送られるNICをroute addコマンドで教えてあげないとならないかもしれません。(OSのDefault routerがWAN側に向いていたりすると、セキュリティー的な問題でマルチキャストパケットを内内接続の別のLANに向けた方がいいと思います。パフォーマンスが求められる場合などもサービスのNICとCluster用のNICは分けたほうが良いと思います。)
- <Sender replicationMode="synchronous" waitForAck="true" />
- Objectの複製について「syncronous」「asyncronouse」「pool」が選べるようです。またwaitForAckは完了まで待つか待たないかを設定できるようです。(未確認)
- <Valve className="〜ReplicationValve" filter=".*\.gif;.*\.js;〜"/>
- Clusterノード間で複製しないものを指定できるようです。普通はこのままで良いでしょう。
- <Valve className="org.apache.catalina.cluster.session.JvmRouteBinderValve" enabled="true" />
- …よくわかりません。とりあえず書いておきました。
- <ClusterListener className="org.apache.catalina.cluster.session.JvmRouteSessionIDBinderListener" />
- …よくわかりません。多分Cluster内でSession Objectを複製する為のものでしょう。
- <Deployer>
- 別のClusterノードから転送されてきたObjectやファイルを格納しておくキャッシュの設定だと思われます。OS側の都合で/tmpなどが使用不可の場合は別の場所にしましょう。
- <ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>
- …よくわかりません。とりあえず書いておきました。
未調査の項目が多くてすいません…。もしかしたら余計な事もやってるかもしれません。
(一応この後の検証方法で問題が出なかったので…。)
次に、ClusterしたいWeb Applicationのweb.xmlに<distributable/>というマークを追加します。
web.xmlはWeb ApplicationのDirectory直下の「WEB-INF」Directory内にあります。
追記する位置は「<display-name>の後、<filter>の前」です。
(場所が間違っているとweb.xmlがDTD違反だと言われ、Web Applicationが立ち上がりません。)
起動・終了
前記の設定を終えたら両Tomcat、httpdを再起動する。
エラーが出なければひとまずOK。
確認
- まず、立ち上がったTomcatの$TOMCAT_HOME/log/catalina.outをチェックし、以下の文言があることを確認する。
Creating ClusterManager for context XXXX using class org.apache.catalina.cluster.session.DeltaManager ※簡単には「DeltaManager」でgrepして、引っかかればOK?
- 先程のSession ID表示jspを実行する。何度かreloadしてSession IDが固定されている事を確認する。
また、jkstatusでも特定のworkerに処理が固定化されていることを確認。
- jkstatusで上記のworkerをDisableにする。
worker nameの右の「E|R」のEをクリックし、次の画面でDisableを選択する。
- もう一度jspページをreloadする。
Session IDのjvmRoute部分以外が変わらなければ成功。
ちなみにこの状態で先程のworkerをActiveに戻してもSessionは自然とは元のworkerにはFail backしない。
(担当しているworkerを直接落とせば別ですが。)
関連項目
- べたつきません:大変参考にさせていただきましたm(__)m
履歴
2007/01/18 -- 初版
技術的雑談へ戻る