tomcatの最近のブログ記事

概要

Tomcat 6 から Tomcat 7 にかけて、context.xml の Manager タグで指定する maxActiveSessions の扱いに変更があった。
Tomcat 6.x to Tomcat 7.x の Migiration Guide には記載されていないので注意。

Tomcat6 での動作

アクティブセッション数が maxActiveSessions を超えるとスワップが開始される。ただし、minIdleSwap 秒経過していないセッションは除く。

Tomcat7 での動作

maxActiveSessions を超えて request.getSession() を呼びだそうとすると TooManyActiveSessionsException 例外が発生する。

Apache Tomcat Configuration Reference 内の maxActiveSessions の記述を見比べてみると、 Tomcat6 と比べ、Tomcat7 では次の記述が追加された。
When the limit is reached, any attempt to create a new session (e.g. with HttpServletRequest.getSession() call) will fail with an IllegalStateException.

詳細

ManagerBase の createSession() で次のコードが追加された。
if ((maxActiveSessions >= 0) &&
        (getActiveSessions() >= maxActiveSessions)) {
    rejectedSessions++;
    throw new TooManyActiveSessionsException(
        sm.getString("managerBase.createSession.ise"),
        maxActiveSessions);
}

概要

OpenVZ (Virtuozzo) サーバ内で Java を起動する際、メモリに余裕があるにも関わらず、以下のエラーメッセージが出力されることがある。
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

原因

アプリケーションに割り当てられるプライベートメモリの上限に引っかかっている。

root で以下のコマンドを実行する。

# cat /proc/user_beancounters
Version: 2.5
    uid  resource                held          maxheld          barrier                limit      failcnt
  1838:  kmemsize            33351771         47149056        215981162            237579278            0
         lockedpages             9727             9727            10545                10545            0
         privvmpages           410414           892808          1048576              1048576          345
         shmpages               11162            11818           255834               255834            0
         dummy                      0                0                0                    0            0
         numproc                  115              198             5272                 5272            0
         physpages             216325           429549                0              1048576            0
         vmguarpages                0                0           524288               524288            0
         oomguarpages          100958           257789           426391  9223372036854775807            0
         numtcpsock                56               79             5272                 5272            0
         numflock                   3               15             1000                 1100            0
         numpty                     4                4              160                  160            0
         numsiginfo                 0               51             1024                 1024            0
         tcpsndbuf             629496          1823912          5039960              7199372            0
         tcprcvbuf            1024856          2261032         50399608             71993720            0
         othersockbuf          174424           321944          2519980              4679391            0
         dgramrcvbuf                0            26456         25199804             25199804            0
         numothersock             194              224             5272                 5272            0
         dcachesize          16874504         32772941         47171603             48586752            0
         numfile                 1404             1775            84352                84352            0
         dummy                      0                0                0                    0            0
         dummy                      0                0                0                    0            0
         dummy                      0                0                0                    0            0
         numiptent                 38               38              200                  400            0
ここで、privvmpages 行の failcnt 値が上昇していることが確認できる。
各パラメータの意味は 【OpenVZ】リソース パラメータ が詳しい。

[VPS] Virtuozzoのメモリはfreeやtopでなくてprivvmpagesを見るべし を参考に、使用できるメモリ量を確認すると、
beans=`cat /proc/user_beancounters | grep priv`
max=`echo $beans | awk '{ print $4;}'`
use=`echo $beans | awk '{ print $2;}'`
let "per=$use*100/$max"
let "mb=$use/256"
let "mmb=$max/256"
echo "privvmpages usage: $mb MB ($per% of $mmb)"
このサーバの場合は
privvmpages usage: 1606 MB (39% of 4096)
となった。

対策

その1: Java の使用メモリを減らす

Java の起動オプションで
java -Xmx1024m -Xms512m
の箇所を調整する。

その2: VPS の privvmpages 値を増やす

レンタル VPS サーバではなく自前で VPS サーバを立ち上げている場合、設定ファイルで privvmpages 値を増やすことができる。
詳細は OpenVZ VPS and Java Memory Error and how to fix it を参照。

参考文献

概要

前回 は、Windows 上で起動した Java プロセスを snmp で監視する方法を説明した。

今回は Linux 上で起動した Tomcat プロセスのメモリを監視する方法について説明する。

動作環境

項目
OS CentOS 5
Java JDK 6 update 10
Tomcat 6.0.20

tomcat 起動時の設定

Tomcat を起動する時の CATALINA_OPTS 変数に次の記述を追加する。
例えば、/etc/tomcat6/tomcat6.conf で次のように記述する。
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.snmp.port=1161 -Dcom.sun.management.snmp.acl.file=/etc/tomcat6/snmp.acl"

/etc/tomcat6/snmp.acl 作成

ファイルを作成する。
acl = {
 {
   communities = public
   access = read-only
   managers = localhost
 }
}
さらに、snmp.acl のパーミッションを 600 に変更する。
# chown roo:root snmp.acl
# chmod 600 snmp.acl
ここまで設定が終わったら、Tomcat を起動する。
# service tomcat6 start

MIB ファイルを置く

/usr/share/snmp/mibs/JVM-MANAGEMENT-MIB.mib を作成する。
# cd /usr/share/snmp/mibs/
# lftpget http://java.sun.com/j2se/1.5.0/docs/guide/management/JVM-MANAGEMENT-MIB.mib

/etc/snmp/snmp.conf 設定

次に snmpd の設定。このファイルの最後へ記述を追加する。
proxy -m /usr/share/snmp/mibs/JVM-MANAGEMENT-MIB.txt -v 2c -c public localhost:1161 .1.3.6.1.4.1.42.2.145
記述を追加したら、snmpd を再起動する。
# service snmpd restart

動作確認

$ snmpwalk -v 2c -c public localhost .1.3.6.1.4.1.42.2.145

出力結果をカスタマイズ

数字で構成される OID 値を文字列に変更できる。
上記の手順で mib ファイルを snmpwalk を実行するホストに置いた後、次のように環境変数を設定する。
.bashrc などに書いておくとよい。
$ export MIBS=ALL
$ snmpwalk -v 2c -c public localhost .1.3.6.1.4.1.42.2.145

参考

概要

web アプリケーションの負荷試験などを行う際、tomcat のセッションの入出力状態をモニターしたくなることがある。

また、Hibernate + Seasar + Wicket でアプリケーション構築時、セッションのデシリアライズがうまくいかないことがあり、tomcat の PersistentManager や StandardSession の動作を追っていくことで問題を発見、修正できることがある。

Linux で tomcat 動作時のデバッグ手順

logging.properties を次のように修正する。
handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

1catalina.org.apache.juli.FileHandler.level = FINEST
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = catalina.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter


#org.apache.catalina.level = INFO
org.apache.catalina.level = FINEST

org.apache.catalina.connector.level = INFO
org.apache.catalina.loader.level = INFO
org.apache.catalina.startup.level = INFO
org.apache.catalina.mbeans.level = INFO

これで、catalina.out に詳細なログが出力される。

Eclipse + WTP + tomcat 環境でのデバッグ方法

Eclipse 上で次の操作を行う。
  • [実行] メニュー ⇒ [実行ダイアログを開く]
  • ダイアログ左側の一番上にある「Apache Tomcat」の中の、「localhost の tomcat v6.0 サーバー」をクリック。
  • ダイアログ右側から「引数」タブを開く。
  • VM引数に次のような記述を追加する。パスは自分の環境に合わせて適宜修正する。
    -Djava.util.logging.config.file="C:\usr\apache-tomcat-6.0.16\conf\my.logging.properties"

次に、c:\usr\apache-tomcat-6.0.16\conf\my.logging.properties ファイルを作成する。
handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

1catalina.org.apache.juli.FileHandler.level = FINEST
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = catalina.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter


#org.apache.catalina.level = INFO
org.apache.catalina.level = FINEST

org.apache.catalina.connector.level = INFO
org.apache.catalina.loader.level = INFO
org.apache.catalina.startup.level = INFO
org.apache.catalina.mbeans.level = INFO

これで tomcat 起動時、catalina.out に詳細なログが出力されるようになる。

ちなみに、catalina.out の場所は workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\logs となる(Eclipse 3.3.2 Preiades All in One の場合)。

概要

apache+tomcat サーバ2台で負荷分散環境設定の記録。

要件

  • 正常時は2台のtomcatサーバで負荷分散。
  • セッションは DB(postgresql) に格納する。
  • 負荷分散時、stickysession 機能により、一ユーザのアクセスは可能な限り、1つの tomcat サーバに固定する。
    • 1台あたりのtomcatサーバで扱うセッション数はなるべく少なくし、余計なメモリを消費しないようにする。

Apacheサーバ設定

/etc/httpd/conf.d/tomcat.conf を作成し、次のように記述した。

ProxyRequest 設定

ProxyRequests off は、リバースプロキシであることを明示。

ProxyPass 設定

ProxyPass 設定項目の解説。

stickysession=JSESSIONID は、tomcat によって生成されたセッションを特定のサーバに紐付けるために使用。
PHP ならPHPSESSIONID、tomcat サーバなら JSESSIONID を指定する。

nofailover=Off は、tomcat サーバ同士でセッション・レプリケーションが行われることを Apache に知らせている。
tomcat5 以上はセッション・レプリケーションに対応している。

timeout=2 は、バランサーのタイムアウト時間の設定 [秒]。

maxattempts=1 は、フェイルオーバーを試みる最大回数の指定。

BalancerMember 設定

ajp://host1:8009 は、接続先のtomcat サーバのホスト名とポート番号。

keepalive=On は、Apache とバックエンドサーバとのコネクションを保持し続けるかどうか。

retry=2 は、バックエンドサーバへの接続失敗時のリトライ回数。

route=r1 は、stickysession に関連した設定。
tomcat サーバにおいて jvmRoute を指定しておくと、セッション生成時に JSESSIONID の後尾にその文字列が付加される。
そして Apache 側で route 指定をしておくことで、Apache は JSESSIONID の後尾文字列を参照し、route 指定がある tomcat サーバへ優先的に接続してくれるようになる。
tomcat 側での jvmRoute 設定を合わせて設定しておくこと(後述)。

loadfactor=10 は複数の BalancerMember に対し、どのくらいの割合で負荷を分散するかを指定する。

tomcatサーバ設定

conf/server.xml で次のように修正する。
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JasperListener" />
  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />

  <Service name="Catalina">
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost" jvmRoute="r1">
      <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true"
          xmlValidation="false" xmlNamespaceAware="false"/>
    </Engine>
  </Service>
</Server>
ホスト host2 上の tomcat では jvmRoute="r2" となる。

webアプリケーション設定

META-INF/context.xml (例: maven2 で管理されたプロジェクトの場合、 src/main/webapp/META-INF/context.xml) で次のように記述する。

PersistentManager 設定

backgroundProcessorDelay はセッションのスワップアウトやバックアップなどが行われる間隔を指定する。
backgroundProcessorDelay * processExpiresFrequency 秒間隔で実行されるらしい。(参考文献)

saveOnRestart は、tomcat 停止時にセッションを(DBへ)保管するかどうか。

maxActiveSessions は、メモリ内に保持するセッション数。

minIdleSwap。セッション数が maxActiveSessions を越えたとき、スワップアウト(DBへの保管)が発生するが、ここで指定した秒数を超えない限りはスワップアウトされない。

maxIdleBackup はセッションが最後に使われてからDBへバックアップされるまでの時間 [秒]。
バックアップが行われてもメモリからは削除されない。

distributable は、アプリケーションからセッションに格納するオブジェクトがシリアライズ可能(Serializable)かどうかを指定する。
true にすると、シリアライズ不可なオブジェクトを HttpSession に格納しようとすると IllegalArgumentException 例外が発生する。

データベース作成

PostgreSQL に対し、データベースとテーブルを作成する。
# /bin/su postgres
$ createdb tomcat
$ psql tomcat

次の SQL を実行する
CREATE TABLE tomcat_sessions (
	session_id character varying(100) NOT NULL,
	valid character(1) NOT NULL,
	maxinactive integer NOT NULL,
	lastaccess bigint NOT NULL,
	app character varying(255),
	data bytea,
	CONSTRAINT tomcat_sessions_pkey PRIMARY KEY (session_id)
)
WITHOUT OIDS;
ALTER TABLE tomcat_sessions OWNER TO postgres;
CREATE INDEX tomcat_session_idx1 ON tomcat_sessions USING btree (app);

参考文献

概要

例えば、次のようなフォームで servlet にリクエストを送信する。
<form method="get" action="/test.servlet">
<input name="query" />
<input type="submit" vallue="送信" />
</form>
そして、Servlet 側で次のように文字列を受け取ると、日本語が文字化けしてしまう。

解決方法

Tomcat5 では、デフォルトでGETパラメータのときは setCharacterEncoding 指定を無視する仕様になったらしい。(ご参考: @IT)

ということで、tomcat の conf/ ディレクトリの中にある server.xml で、Connector に「useBodyEncodingForURI="true"」を追加すると改善される。
   <Connector acceptCount="100" connectionTimeout="20000"
    		disableUploadTimeout="true" enableLookups="false"
    		maxHttpHeaderSize="8192" maxSpareThreads="75"
    		maxThreads="150" minSpareThreads="25" port="80"
    		redirectPort="8443"
    		useBodyEncodingForURI="true" />

Eclipse WTP での設定方法

Eclipse 3.3.2 の WTP で tomcat を起動している場合は、左側ペインの「パッケージ・エクスプローラ」の中に「Servers」プロジェクトがあるので、その中の server.xml を修正する。

概要

jconsoleで Windows PC から Linux サーバ上で稼動している Tomcat サーバに対して接続し、リソース使用状態を監視する方法についてまとめた。

このjconsole、「接続しようとしても接続できない」という問題に結構出会う。
しかも「何が問題か」を教えてくれない。
以前にもこの現象に出会って時間を取られてしまったにもかかわらず、メモを残していなかったので、また調査するはめに...orz

無駄な時間を今後費やすことがないよう、自分が出会ったトラブルについてまとめた。

jconsoleとは

Sun JDK 6 の中には、コンパイラ javac の他にも、いくつか役に立つツールが含まれている。
jconsole は、Java プログラムに対し、次のような情報を得ることができる。
  • パフォーマンス情報
  • メモリの使用状態
  • 稼働中のスレッドに関する情報
  • JMX
リモートのホスト上のプログラムの監視も行える。

Sun による解説ページはこちら

Javaサーバが稼動するホストにおける準備

まず、hostname -i を実行する。127.0.0.1 が返ってきた場合、まずこれを解決する必要がある。
# hostname -i
127.0.0.1

/etc/hosts を修正する

127.0.0.1 が返ってくる場合、/etc/hosts を見ると、おそらく次のように記述されていると思う。
# cat /etc/hosts
127.0.0.1       localhost foo.bar.jp
これを、次のように修正する。foo.bar.jp に対し、このホストのIPアドレスを記述する。
# cat /etc/hosts
127.0.0.1       localhost
192.168.1.2   foo.bar.jp
再度、hostname -i を実行して確認し、ホストのIPアドレスが返ってきたらOK。
# hostname -i
192.168.1.2

ホストに複数のIPアドレスが設定されている場合は?

例えば1つのホストにローカルIPとグローバルIPのように2つが設定されているようなケースでは、「管理用PCから接続するときの IPアドレス」が hostname -i で返ってこなければならない。

Javaサーバの起動スクリプトの修正

tomcat を起動するときの Java のパラメータに、次のオプションを追加する。
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=7900
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

Fedora 8 の場合

Fedora 8(F8) に対し rpm で tomcat5 をインストールした場合、起動時パラメータの修正は/etc/tomcat5/tomcat5.conf で行う。

例えば、ファイルの最後尾に次の記述を追加する。
改行せず、1行につなげる
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote
        -Dcom.sun.management.jmxremote.port=7900
        -Dcom.sun.management.jmxremote.ssl=false
        -Dcom.sun.management.jmxremote.authenticate=false"

設定後、tomcat を起動する。
# service tomcat start

ファイアウォールに穴をあける

例えば、Java サーバに iptables でパケット・フィルタリングを実施している場合、「管理PCから jmx ポートへのアクセスを許可」の設定を行う必要がある。

ここで注意しなければならないのが、この機能では次の2つのポートを空ける必要があること。
  • com.sun.management.jmxremote.port で指定したポート (上記例では 7900)
  • そのポート~65535
したがって、例えば管理PC 192.168.1.100 が tomcat サーバのホスト 192.168.1.2 に jconsole で接続する場合、 192.168.1.2 上の /etc/sysconfig/iptables に次のような記述を加える。
改行せず、1行につなげる
-A RH-Firewall-1-INPUT -m state --state NEW 
               -m tcp -p tcp -s 192.168.1.100 --dport 7900:65535 -j ACCEPT
これで準備完了!

jconsole の使い方

Windows PC 上で jconsole.exe を起動する。jconsole.exe は次の場所にある。
C:\Program Files\Java\jdk1.6.0_05\bin
次の画面が表示されたら、

「リモートプロセス」のところに、192.168.1.2:7900 のようにホスト名とポート番号を入力し、[接続] ボタンを押す。

トラブルシューティングまとめ

jconsole でリモートの Java サーバに接続しようとしても接続できない時、次の点をチェックする。
  1. サーバ側で hostname -i を実行したときに表示される IP アドレスは、 jconsole で接続しにいくときと同じ IP アドレスか。
    • /etc/hosts を修正する。
    • ホスト名を変更する場合は、次の手順で行う。
      # hostname
      foo.bar.jp (現在のホスト名が表示される)
      
      # hostname -i
      200.100.50.25   このホストの eth0 の IPアドレスが表示される
      
      # hostname foo.bar.local    ホスト名を変更する
      
      # hostname
      foo.bar.local
      
      # hostname -i
      192.168.1.2     このホストの eth1 の IPアドレスが表示される
      

      tomcat 起動後に /etc/hosts を修正したときは、tomcat を再起動すること。
  2. ファイアウォールで適切なポート番号を空けているか。

接続時にパスワードを要求するには?

java 起動時オプションから -Dcom.sun.management.jmxremote.authenticate=false を削除する。

次に、サーバ上で次の作業を行う。
# cd  /usr/java/default/jre/lib/management
# less jmxremote.access
ユーザ名 monitorRole に対し、readonly の権限が付与されている。
このユーザに対し、パスワードを設定する。

# cp jmxremote.password.template jmxremote.password
# vi jmxremote.password
最後尾に次のようにパスワードを設定する。

プレインテキストであるため、普段使っているパスワードを使用しないこと!
monitorRole hogehoge

# chmod 600 jmxremote.password
# chown tomcat jmxremote.password
なお、jdk を rpm でアップグレードした際、この辺りの修正は引き継がれないので、その際は忘れずにチェックすること。
または、JAVA 起動時のオプションにて、管理ファイルやパスワードファイルのありかを指定できる。 詳細は Sun の解説ページ を参照のこと。

tomcatでDB接続

| | コメント(0) | トラックバック(0)
Windows 環境において、tomcat で PostgreSQL に JNDI 経由で接続する手順をまとめた。

導入環境
tomcat インストールディレクトリ c:/usr/apache-tomcat-5.5.25
webapp ディレクトリ /home/webapps/test/WebContent
webapp を公開するパス /test

1. postgresql jdbc ドライバのインストール

  • PostgreSQL JDBC ドライバのダウンロードサイト から、使用している PostgreSQL サーバと同じバージョンの JDBC ドライバをダウンロードする。
    自分の場合は postgresql-8.2-508.jdbc4.jar をダウンロードした。
  • postgresql-8.2-508.jdbc4.jar を C:\usr\apache-tomcat-5.5.25\common\lib\ に置く。

2. context.xml 設定

apache-tomcat-5.5.25/conf/Catalina/localhost/test.xml を、次のような内容で作成する。

Eclipse All-In-One での設定方法

Eclipse All-In-One を使っている場合、プロジェクトのプロパティの「Tomcat」を開き、「その他の情報」のところへ次のように入力するとよい。

3. web.xml 設定

/home/webapps/test/WebContent/web.xml に次の記述を追加する。

4. プログラムからの呼び出し

2012年11月

        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30