Tomcat5めも

作成 2003/1/6
更新 2003/1/8

Tomcat5の新機能についてメモってきます。

クラスタリング

クラスタリングとは、システムの信頼性を高めるための仕組みです。複数のサーバを起動しておいて、1つがクラッシュしてしまった場合でも他のサーバで処理を引き継げるようにします。JavaのWebアプリケーションサーバでクラスタを組むときに問題になるのは、セッションデータです。一台がダウンしたときに、別のサーバで処理をひきつぐにしても、それまでのセッションデータがないことには、処理が引き継げません。そのために、セッションデータをサーバ間で複製しておく仕組みをセッションレプリケーションと言います。Tomcatにはこれまで、セッションレプリケーションの仕組みがなく、この点が商用アプリケーションに比べて、機能的に見劣りする部分でした。しかし、Tomcat5になり、とうとうTomcatにセッションレプリケーションの機能が実装されました。

ここでは、マニュアル(Clustering)の手順を元にセッションレプリケーションを試してみます。

実験環境

Tomcatを2つ準備

本当は複数台のPCで試すのがよいのですが、今手元に 1台しかPCがないので、ここでは 1台のPCでTomcatを 2つ起動して試します。 1台のPCで複数のTomcatインスタンスを起動する場合は、一方のTomcatのserver.xmlの各種ポート設定を競合しないようにずらしてやる必要があります。ここでは、一方のTomcatのserver.xmlで競合するポートに全部10000を足しました。

まず、Tomcatを2つ用意します。ここでは、簡単のため、Tomcatをフォルダごとまるごとコピーして2つにしました(まるごとコピーしなくても起動パラメータで変えるとは思いますが)。

クラスタリング設定

以下の設定を2つのTomcat両方で行います。

クラスタ設定を行います。conf/server.xmlのCluster要素とValve(ReplicationValve)要素のコメントをはずします(以下のコメントをはずす)。

365行目付近

      <!-- When uncommenting the cluster, REMEMBER to uncomment the replication Valve below as well
        

        <Cluster  className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
                  name="FilipsCluster"
                  debug="10"
                  serviceclass="org.apache.catalina.cluster.mcast.McastService"
                  mcastAddr="228.0.0.4"
                  mcastPort="45564"
                  mcastFrequency="500"
                  mcastDropTime="3000"
                  tcpThreadCount="2"
                  tcpListenAddress="auto"
                  tcpListenPort="4001"
                  tcpSelectorTimeout="100"
                  printToScreen="false"
                  expireSessionsOnShutdown="false"
                  useDirtyFlag="true"
                  replicationMode="synchronous"
        />
        -->

417行目付近

        <!--
        <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
               filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>

        -->

次に、クラスタリング対象にするWebアプリケーションのweb.xmlに<distributable/>要素を追加します。ここでは、servlets-examplesのサンプルを使うので、servelts-examplesのweb.xmlを修正しました(descriptionの後に追加)。

<web-app>

    <display-name>Servlet 2.4 Examples</display-name>
    <description>
      Servlet 2.4 Examples.
    </description>

    <distributable/>

    ....

競合するポートをずらす(1台のPCで行う場合)

server.xmlで競合するポートをずらします。HTTPポートだけでなく、いくつかあります。なお、Cluster要素のポートについては、tcpListenPort属性のポート番号のみずらせばよいです。ずらし忘れがあると、起動時にJVM Bindエラーが出ます。

Tomcatの起動

両方のTomcatをstartup.batなどから起動します。

ブラウザでアクセス

ここでは、servelts-examplesのSessions Exampleを使います。ブラウザで、http://localhost:8080/servlets-examples/servlet/SessionExample にアクセスします。NameとValueに値を入力(ここではkey1、value1としました)し、クエリ送信ボタンを押します。


Tomcatのコンソールを見ると、Tomcat1の方に保存されたセッションがTomcat2に複製(レプリケート)されているのが分かります。

Tomcat1(HTTP 8080で起動) Tomcat2(HTTP 18080で起動)
情報: Average cluster serialize/send time=30 ms for 1 requests (30ms).
2004/01/07 1:57:32 org.apache.catalina.cluster.session.SimpleTcpReplicationManag
er log
情報: writeObject() storing session D058BDD50BD314D512A4DD0EF8D99B51
2004/01/07 1:57:32 org.apache.catalina.cluster.session.SimpleTcpReplicationManag
er log
情報:   storing attribute 'key1' with value 'value1'
2004/01/07 1:57:32 org.apache.catalina.cluster.tcp.ReplicationValve addClusterSe
ndTime
情報: Average cluster serialize/send time=20 ms for 2 requests (40ms).
情報: readObject() loading session D058BDD50BD314D512A4DD0EF8D99B51
2004/01/07 1:57:32 org.apache.catalina.cluster.session.SimpleTcpReplicationManag
er log
情報:   loading attribute 'key1' with value 'value1'
2004/01/07 1:57:32 org.apache.catalina.cluster.session.SimpleTcpReplicationManag
er log
情報: Received replicated session=ReplicatedSession id=D058BDD50BD314D512A4DD0EF
8D99B51 ref=StandardSession[D058BDD50BD314D512A4DD0EF8D99B51]
        name=key1; value=value1
        LastAccess=1073408252953

Tomcat1がダウンだ!

Tomcat1がダウンしたことにします。Tomcat1の方をshutdown.batなどで、シャットダウンします。ブラウザで同じURLにアクセスすると、サーバーはダウンしているので、エラー画面になります(もし前と同じ画面が表示されてたら、それは、おそらくブラウザのキャッシュです)。

Tomcat2で代わりに処理を継続したことにする

Tomcat2の方(ポート18080)にアクセスすると、セッション情報(さきほど入力したkey、value)をひきついだまま、処理が継続できます。

Tomcat1が復旧したことにする

Tomcat1を起動します。Tomcat1(ポート8080)にアクセスすると、何事もなかったようにセッションをひきついで、処理が継続できます。

補足事項

ここでは、手打ちでTomcat1、Tomcat2のURLを変えましたが、実際はロードバランサーを含めたクラスタリングを組んで、クライアントからは、同じURLでそのまま処理が継続しているように見えるようにするでしょう。

また、「Tomcat2で代わりに処理を継続したことにする」で、手打ちでURLを変えて、同じセッションを引き継げたのは同じURL(localhost)で試しているからで、別のPCで単純にこれを行った場合は、URLが変わるので、クッキーが異なる→セッションIDが異なるので、セッション情報はひきつげません。

マニュアルにあるように、Tomcatのセッションレプリケーションは、PersistenceManager、JDBCManager、SimpleTcpClusterを使う3つの方法がありますが、ここでは最後のSimpleTcpClusterを使う方法(インメモリの転送)を行っています。

また、現在のセッションレプリケーションは、all-to-all というセッション全部コピーという方式が使われています。この方式は、クラスタ構成ノードが増えると、パフォーマンスが問題になるので、将来のリリースでは異なる方式がサポートされる予定らしいです。5.0.18以降ではセッションにバインドしたデータのみが転送されます。

感想

Tomcatでクラスタリング。。。商用サーバはますます高機能な差別化が求められるようになりますね。といっても、Tomcatのクラスタは、まだ実装されたばかりなので、実際に運用で利用できるようになるのは、もう少し先かもしれません。性能評価しているサイトなどもあり、比較的大きなデータでもそれほど性能劣化はない結果になってますが、もうちょっと現実的なシナリオで試してみないとなんとも言えない気もします(セッションに大きなデータを入れたあと、ちまちまセッションバインドするような場合。自分で試せって(?)。

次はロードバランスを含めた構成を試してみたいと思います。

参考

IT Pro 第4回 クラスタ機能で商用APサーバーに迫るTomcatの新版5
http://itpro.nikkeibp.co.jp/members/SI/oss/20031225/1/

ロードバランス

ロードバランスとは、サーバーを分散させたときに特定のサーバに負荷がかかりすぎないように、リクエストを振り分けることです。ここでは、Tomcatの前面にApacheを立てて、mod_jk2を使ったロードバランスを行ってみます。

実験環境

上の構成に以下を追加します。

mod_jk2は本来Apacheとバージョンをそろえるべきです(コンパイルして)が、ここではバイナリ版で一番バージョンが近いものを使っています。長時間は稼動させていないですが、一応問題なく動いてます。なお、未だPCは一台構成です。

基本的なApache + Tomcat連携

基本的なApachetとTomcat連携方法については、ここでは触れません。以下のページを参考にしてください。

http://www.atmarkit.co.jp/fjava/rensai2/jspservlet03/jspsevlet03_5.html

http://www.alles.or.jp/~torutk/oojava/sabbath/tomcatConnector.html

次にロードバランス用に若干、設定を行います。

workers2.propertiesの編集

%Apache2%/conf/workers2.propertiesを編集します。TCPソケットチャンネルを2つ設定しています。なおapache側のjkstatus画面を利用したいので、uriは全部(/*)を指定せず、適当なWebアプリのパスなりを指定してください。ここでは、/servlets-examples/*を指定しています。

[status:]
[uri:/jkstatus/*]
group=status:
[shm:]
disabled=1
[channel.socket:localhost:8009]
[channel.socket:localhost:18009]
[uri:/servlets-examples/*]

ポートは、Tomcatのserver.xmlで指定されているConnectorのポートです。一方のTomcatでは8009(デフォルト)で、もう一方のTomcatでは18009を指定しています。

tomcat1のserver.xml

    <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" 
               enableLookups="false" redirectPort="8443" debug="0"
               protocol="AJP/1.3" />

tomcat2のserver.xml

    <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 -->
    <Connector port="18009" 
               enableLookups="false" redirectPort="8443" debug="0"
               protocol="AJP/1.3" />

実行する

2つのTomcatを起動した後、Apacheを起動します(一応この順序がエラーの出ない順序)。SessionExample(http://localhost/servlets-examples/servlet/SessionExample)などにアクセスします(別に何でもいいです)。Apacheはデフォルトの80番ポートで起動しているので、アクセスするURLのポート番号は省略しています。

ブラウザで、http://localhost/jkstatusにアクセスすると、mod_jk2の情報が表示されます。SessionExampleにアクセスするたびに、2つのTomcatに処理が振り分けられていくのがわかります。


なお、この設定では、ブラウザで、アクセスするたびに2つのTomcatに均等に(順番に)処理が割り振られているのがわかると思います。これはlb_factorの値が同じ(デフォルトの1)だからです。lb_factorが小さいほど優先度が高くなり多く処理が割り振られるようになります。この値は、workers.propertiesで指定することが出来ます。

[channel.socket:localhost:8009]
lb_factor=1
[channel.socket:localhost:18009]
lb_factor=2

lb_factorに0を指定した場合、(Tomcatから応答がある限り)全てそのTomcatに処理が割り振られます。これは、ホットスタンバイ的な構成に利用できます。

セッションアフィニティ

サーブレット仕様では、同じセッションのリクエストの処理は、同じサーバー上で処理することになっています。つまり、同じクライアントからのリクエストは同じサーバーに処理を分配するわけです。この仕様は、サーブレットコンテナのロードバランス、フェールオーバーなどの設計、実装を助けるもので、同じクライアントからほぼ同時のリクエストがきた場合などにも対処しやすくなります。このリクエストの分散方式はセッションアフィニティ (スティッキーセッション)と呼ばられます。

上のmod_jk2の動作は、順番に処理を振り分けているので、セッションアフィニティではありません。セッションアフィニティな仕組みでロードバランスを行うには次のようにします。

server.xmlのEngine要素のjvmRouteにクラスタ内でユニークな名前を指定します。

tomcat1のserver.xml

    <Engine name="Catalina" defaultHost="localhost" debug="0" 
     jvmRoute="tomcat1">

tomcat2のserver.xml

    <Engine name="Catalina" defaultHost="localhost" debug="0" 
     jvmRoute="tomcat2">

server.xmlには<!-- -->内の説明記述にも、こういう記述があるので、間違えてコメントの中だけ直さないように(これでしばらく悩んだ。馬鹿?)。

でもって、Apache側のworkers2.propertiesでtomcatId(上のjvmRouteの値)を指定します。これによりmod_jk2が同じセッション中のリクエストは同じTomcatに処理を振り分けてくれます。

[status:]
[uri:/jkstatus/*]
group=status:
[shm:]
disabled=1
[channel.socket:localhost:8009]
tomcatId=tomcat1
[channel.socket:localhost:18009]
tomcatId=tomcat2
[uri:/servlets-examples/*]

参考

Tomcatハンドブック 10章 クラスタリング


TOP