2014年2月7日

DynamoDB で tomcat の session を管理する

DynamoDB で session 管理

AWS SDK for Java のドキュメントに、「Manage Tomcat Session State with Amazon DynamoDB」という項目があります。
http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-tomcat-session-manager.html

名前の通り Tomcat の Session を DynamoDB で管理する方法がありました。こちらを試してみます。

ソースは github にあります。
https://github.com/aws/aws-dynamodb-session-tomcat

Eclipse で github からソース取得して、「プロジェクトのインポート」をすることでプロジェクトとして認識されます。
pom.xml があるので、プロジェクトの右クリックメニューから maven プロジェクトへ変換できますのでしてしまいましょう。

変換後、pom.xml を右クリックして maven build でゴールを package とすれば target ディレクトリに aws-dynamodb-session-tomcat-1.0.0.jar が作られます。

前述の AWS のドキュメントに
Copy AmazonDynamoDBSessionManagerForTomcat-1.x.x.jar to the lib directory of your Tomcat installation.
とありますのでこの jar を tomcat の lib ディレクトリに入れましょう。WEB-INF/lib の中に入れても起動時に java.lang.ClassNotFoundException となるようです。
注意が必要なのはこの aws-dynamodb-session-tomcat-1.0.0.jar から DynamoDB へアクセスするわけですから AWS SDK が必要となるため、AWS SDK およびその依存ライブラリも lib へ入れておきましょう。(Eclipse で AWS Java Web Project を作成する際に DynamoDB Session Management を選択して作成された jar には依存ライブラリも含まれているようです)

次に context.xml を編集して、Session 管理に DynamoDB を使用するように修正します。

設定できる項目は以下です。AccessKey/SecretKey、CredentialsFile を設定していなかった場合は DefaultAWSCredentialsProviderChain を使用するようですので、EC2 上で IAM Role を使いたい場合は何も設定しなければいいですね。
awsAccessKeyアクセスキー
awsSecretKeyシークレットキー
awsCredentialsFileアクセスキー/シークレットキーが書かれたファイルパス
endpointDynamo DB のエンドポイント
regionId使用するリージョン
tableテーブル名、省略した場合 Tomcat_SessionState
createIfNotExisttrue の場合、存在しない場合に新規作成する
readCapacityUnitsテーブル作成時に設定する read capacity units
writeCapacityUnitsテーブル作成時に設定する write capacity units

ドキュメントにもサンプルがありますが、例えばこんな感じです。
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Manager className="com.amazonaws.services.dynamodb.sessionmanager.DynamoDBSessionManager"
             endpoint="dynamodb.ap-northeast-1.amazonaws.com"
             table="hogehoge_session"
             createIfNotExist="true"
              />
</Context>

これで起動すれば Sessin は DynamoDB で管理されるようになります。Auto-Scaling で試してみたいですね!

DynamoDBLocal で session 管理

さて、context.xml に endpoint があったので思いついた人もいるかと思いますが、これを DynamoDBLocal で試します。
endpoint を DynamoDBLocal に向けるだけなので簡単です。
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Manager className="com.amazonaws.services.dynamodb.sessionmanager.DynamoDBSessionManager"
             awsAccessKey="teketou"
             awsSecretKey="teketou"
             endpoint="http://localhost:8000"
             table="hogehoge_session"
             createIfNotExist="true"
              />
</Context>
DynamoDBLocal に session を保持する意味はあまりわかりませんが、ビーチでも飛行機の中でもコーディングできるようにはなると思います!

2014年2月4日

tomcat のスレッドダンプ

EC2 上の tomcat のスレッドダンプを取ってみましょう。
スレッドダンプを取得するには JDK に含まれる jsp で調べたプロセス ID を、jstack に渡すことで出力されます。

EC2 にデフォルトではいっているのは JRE6 なので、まずは JDK7 と tomcat7 を入れてみます。(JDK6 でも OK です)
$ sudo yum install java7-devel tomcat7
(中略)
Complete!
$ java -version
java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.14) (amazon-65.1.11.14.57.amzn1-x86_64)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)

インストールしたばかりでは Java6 のままです。
alternatives コマンドを使って切り替えましょう。
$ sudo alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.6.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java

Enter to keep the current selection[+], or type selection number: 2
$ java -version
java version "1.7.0_51"
OpenJDK Runtime Environment (amzn-2.4.4.1.34.amzn1-x86_64 u51-b02)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)

無事にバージョンが切り替わりました。
Tomcat7 を起動します。
$ sudo service tomcat7 start

jps で スレッドダンプを表示させたいプロセスの ID を調べます。
$ jps
2036 Jps

出てこないので sudo で試してみます。
$ sudo jps
1842 Bootstrap
2043 Jps

出てきました。
Bootstrap が tomcat の起動プロセスっぽいのでこれに対して jstack を実行してみます。
$ sudo jstack 1842
1842: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding

怒られました。ユーザが違うからでしょうか。tomcat ユーザで実行してみます。
$ sudo sudo -u tomcat jps
2059 Jps
1842 Bootstrap
$ sudo sudo -u tomcat jstack 1842
2014-02-04 03:50:55
Full thread dump OpenJDK 64-Bit Server VM (24.45-b08 mixed mode):

"Attach Listener" daemon prio=10 tid=0x00007f9e00003000 nid=0x821 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"ajp-bio-8009-AsyncTimeout" daemon prio=10 tid=0x00007f9e1c439000 nid=0x743 waiting on condition [0x00007f9e19430000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:148)
        at java.lang.Thread.run(Thread.java:744)

"ajp-bio-8009-Acceptor-0" daemon prio=10 tid=0x00007f9e1c437800 nid=0x742 runnable [0x00007f9e19531000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
        at java.net.ServerSocket.implAccept(ServerSocket.java:530)
        at java.net.ServerSocket.accept(ServerSocket.java:498)
        at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
        at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:216)
        at java.lang.Thread.run(Thread.java:744)

"http-bio-8080-AsyncTimeout" daemon prio=10 tid=0x00007f9e1c435800 nid=0x741 waiting on condition [0x00007f9e19632000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:148)
        at java.lang.Thread.run(Thread.java:744)

"http-bio-8080-Acceptor-0" daemon prio=10 tid=0x00007f9e1c434800 nid=0x740 runnable [0x00007f9e19733000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
        at java.net.ServerSocket.implAccept(ServerSocket.java:530)
        at java.net.ServerSocket.accept(ServerSocket.java:498)
        at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
        at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:216)
        at java.lang.Thread.run(Thread.java:744)

"ContainerBackgroundProcessor[StandardEngine[Catalina]]" daemon prio=10 tid=0x00007f9e1c427800 nid=0x73f waiting on condition [0x00007f9e19834000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1508)
        at java.lang.Thread.run(Thread.java:744)

"GC Daemon" daemon prio=10 tid=0x00007f9e1c370000 nid=0x73d in Object.wait() [0x00007f9e1af40000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000e97919d0> (a sun.misc.GC$LatencyLock)
        at sun.misc.GC$Daemon.run(GC.java:117)
        - locked <0x00000000e97919d0> (a sun.misc.GC$LatencyLock)

"Service Thread" daemon prio=10 tid=0x00007f9e1c09a800 nid=0x73b runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=10 tid=0x00007f9e1c098000 nid=0x73a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=10 tid=0x00007f9e1c096000 nid=0x739 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x00007f9e1c094000 nid=0x738 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=10 tid=0x00007f9e1c071000 nid=0x737 in Object.wait() [0x00007f9e1bc06000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000e9400570> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
        - locked <0x00000000e9400570> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)

"Reference Handler" daemon prio=10 tid=0x00007f9e1c06f000 nid=0x736 in Object.wait() [0x00007f9e1bd07000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000e9400608> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:503)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
        - locked <0x00000000e9400608> (a java.lang.ref.Reference$Lock)

"main" prio=10 tid=0x00007f9e1c009000 nid=0x734 runnable [0x00007f9e26002000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
        at java.net.ServerSocket.implAccept(ServerSocket.java:530)
        at java.net.ServerSocket.accept(ServerSocket.java:498)
        at org.apache.catalina.core.StandardServer.await(StandardServer.java:452)
        at org.apache.catalina.startup.Catalina.await(Catalina.java:779)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:725)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)

"VM Thread" prio=10 tid=0x00007f9e1c06a800 nid=0x735 runnable

"VM Periodic Task Thread" prio=10 tid=0x00007f9e1c0a5800 nid=0x73c waiting on condition

JNI global references: 178


無事出力されました。
tomcat のスレッドダンプを取得する場面は固まったりするピンチな場面が考えられますが、そんなときのためにとり方を覚えておきたいですね。