最近、アプリケーション基盤の調整をしていますが、そのまた一環で、
Dropwizard で session clustering を行うようにしたいと思います。
Dropwizard で 可能な session clustering 方法
-
JDBC を使って RDB に保存する
java - Dropwizard Session Clustering - Stack Overflow -
MongoDB に保存する MongoDB session clustering in Dropwizard - Google グループ
-
Hazelcast に保存する martindow/dropwizard-hazelcast-session: A Dropwizard bundle for providing clustered session replication using Hazelcast distributed maps.
Dropwizard は Jetty を使っているので、Jetty で提供されている clustering 機能はだいたい使えます。
現在、MongoDB を使用しているため、MongoDB に保存する方式をとりたいと思います。
参考記事
前提.使用ライブラリ
前提となるライブラリの verioon を記載します。
- mongo-java-driver
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.2.2</version>
</dependency>
- dropwizard
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-servlets</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-assets</artifactId>
<version>1.0.6</version>
</dependency>
jetty-nosql の、MongoSessionManager を使って実装する
使っている、mongo-driver
の関係で以下だと、私の環境ではエラーとなりました。
mongo-driver
の verison が 2系であれば以下の実装でも動作するかと思いますので記載しておきます。
pom へ依存関係を追加
参考記事のMongoSessionManager
は jetty-nosql
に含まれています。
Dropzaird 1.0.6 の jetty の version は
Maven Repository: io.dropwizard » dropwizard-jetty » 1.0.6
を確認する限り、9.3.9.v20160517
で、
jetty-nosql
は、dropwizard の maven dependency には含まれないので、以下をpomの依存関係に追加しました。
jetty-nosql
が依存するライブラリ群は、Dropwizard 側で依存している、or Mongo Driver は別途依存性を定義しているため、
除外しています。 1
当初除外していなかったのですが、これが原因で、jettyのクラスがアプリケーションから見えず、エラーとなりました。
- mongo-driver-v2 で動けばOKなら
<!-- mongo-driver-v2 で動けばよいなら -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
<version>9.3.9.v20160517</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
</exclusion>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-common</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
</exclusion>
</exclusions>
</dependency>
- mongo-driver-v3 でも動かすなら
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
<version>9.3.16.v20170120</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
</exclusion>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-common</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
</exclusion>
</exclusions>
</dependency>
- 注意点
現在(2017/02/17時点)での、jetty-nosql のversionの最新は、
jetty-9.4.2.v20170215
ですが、9.3.0
>9.4.0
で Session 関連 API に結構な変更が入っており、
Method 互換性がありません。
jetty の version と合わせてたものをちゃんと持ってくることをお奨めします。
実装
Bundle に 以下の通り、実装しました。
-
Bundle.java (抜粋)
public class MyBundle implements ConfiguredBundle<MyConfiguration> { @Override public void initialize(Bootstrap<?> bootstrap) { // Do Nothing... } @Override public void run(WicketConfiguration configuration, Environment environment) throws Exception { MutableServletContextHandler context = environment.getApplicationContext(); // Mongo Session Manager 登録 setSessionHandlerTo(environment); environment.jersey().disable(); } /** * setSessionHandlerTo * * @param env */ private void setSessionHandlerTo(Environment env) throws UnknownHostException { // Set SessionHandler MongoClientURI mongoURI = new MongoClientURI(MongoDBResource.getUriString()); // Not Close... MongoClient mongoClient = new MongoClient(mongoURI); // 非推奨だが、ライブラリが対応していないので使う.. DB db = mongoClient.getDB("jetty_session"); DBCollection collection = db.getCollection("jetty_session_collection"); env.lifecycle().addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() { @Override public void lifeCycleStarting(LifeCycle event) { log.info("lifeCycleStarting start event = {}", event); if (!(event instanceof Server)) { return; } Server server = (Server) event; MongoSessionIdManager sessionIdManager = new MongoSessionIdManager(server, collection); MongoSessionManager sessionManager = null; try { sessionManager = new MongoSessionManager(); } catch (UnknownHostException e) { throw new IllegalStateException(e); } sessionIdManager.setWorkerName("node1"); sessionIdManager.setScavengePeriod(1800000L); sessionIdManager.setScavengeBlockSize(0); sessionIdManager.setPurge(true); sessionIdManager.setPurgeDelay(1800000L); sessionIdManager.setPurgeInvalidAge(1800000L); sessionIdManager.setPurgeValidAge(5400000L); sessionIdManager.setPurgeLimit(0); sessionManager.setSessionIdManager(sessionIdManager); sessionManager.setPreserveOnStop(true); env.servlets().setSessionHandler(new SessionHandler(sessionManager)); } }); } }
-
説明1
MongoSessionIdManager
の生成には、Server クラスのインスタンスが必要です。
env.getApplicationContext().getServer();
でもJetty Serverにはアクセスできたのですが、
StartServer 前だからなのが、NullPointerException が発生しました。
java - Dropwizard Session Clustering - Stack Overflow
を確認したところ、AbstractLifeCycle.AbstractLifeCycleListener
を使って、JDBCSessionManager
を設定していたので、
この方法を参考に、設定実装を記述しました。 -
説明2 処理の流れは、
1.MongoSessionIdManager
を生成して、MongoSessionManager
に設定。
2.SessionHandler
の コンストラクタに、MongoSessionManager
を設定して、SessionHandler
を生成する。
となります。2
この流れが9.4.0
からだと変わっています。
動かして、NoSuchMethod エラーが発生する場合の対処
サーバを起動したところ、以下の例外が発生しました。
Exception in thread "main" java.lang.NoSuchMethodError: com.mongodb.DBCollection.ensureIndex(Lcom/mongodb/DBObject;Lcom/mongodb/DBObject;)V
at org.eclipse.jetty.nosql.mongodb.MongoSessionIdManager.<init>(MongoSessionIdManager.java:186)
Difference between createIndex() and ensureIndex() in java using mongodb - Stack Overflow
に記載がありますが、mongo driver の 3.0 から メソッドが削除されています。
Github で Issue がないか確認したところ下記がありました。
MongoSessionIdManager uses deprecated ensureIndex · Issue #641 · eclipse/jetty.project
Patch があてられた version があり、そちらにpom の version を 変更したところ、解消されました。
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
<version>9.3.16.v20170120</version>
</dependency>
MongoSessionIdManager の 設定について
- 設定値
Session Clustering with MongoDB
から抜粋します。
MongoSessionIdManager
、MongoSessionManager
に、跨って設定メソッドは存在しています。
設定値 | 説明 | デフォルト |
---|---|---|
scavengeDelay | Sessionデータを削除にかける時間 | 不明(MongoSessionIdManagerから設定できない) |
scavengePeriod | Sessionデータを削除する間隔 | 30分(ms指定) |
scavengeBlockSize | 各削除クエリを制限するセッションIDの数。 メモリに非常に多くのセッションがある場合、これを0以外の値に設定すると、削除を複数のクエリに分割することで削除をスピードアップできます。 デフォルトは0で、すべてのセッションIDが1つのクエリで考慮されます。 | 0 |
purge (Boolean) | settionストアから無効なセッションを完全に削除するか否か | true |
purgeDelay | Purge処理の実行間隔 | 1日(ms指定) |
purgeInvalidAge | 無効なセッションがパージ対象となるまでの期間 | 1日(ms指定) |
purgeValidAge | どれくらいまで遡って、Purge処理の対象とするか指定する期間値 | 336日(ms指定)2 |
purgeLimit | パージクエリから返されるアイテムの数を表す整数値。デフォルトは0で、無制限です。 期限切れの古いセッションが多い場合、この値を設定すると、パージ処理が高速化される可能性があります。 | 0 |
preserveOnStop | SeesionManagerが停止した時に全てのセッションを保持するか否か | true |
[3] 相当、意味がよくかわってないです。。
MongoDB に記録される Session データ
Mongo DB に 保存されたSession データは、以下の通りです。
{
"_id" : ObjectId("58a6e651fb556ea0b033964a"),
"id" : "node11enougmx2cmn1fyywq690pz76",
"created" : NumberLong("1487332945990"),
"valid" : true,
"context" : {
"::*" : {
"__metadata__" : {
"version" : NumberLong(2)
},
"wicket:Key[type=org%2Eapache%2Ewicket%2Eprotocol%2Ehttp%2EWicketFilter, annotation=[none]]:session" : BinData(0,"..."),
"wicket:Key[type=org%2Eapache%2Ewicket%2Eprotocol%2Ehttp%2EWicketFilter, annotation=[none]]:wicket:persistentPageManagerData - Key[type=org%2Eapache%2Ewicket%2Eprotocol%2Ehttp%2EWicketFilter, annotation=[none]]" : BinData(0,"..."),
"Wicket:SessionUnbindingListener-Key[type=org%2Eapache%2Ewicket%2Eprotocol%2Ehttp%2EWicketFilter, annotation=[none]]" : BinData(0,"...")
}
},
"maxIdle" : -1,
"expiry" : NumberLong(0),
"accessed" : NumberLong("1487332957425")
}
-
説明1
maxIdle
値が-1
なので、無期限でsessionは維持されます。
mongoSessionManager#setMaxInactiveInterval(1800);
で設定すると、以下のようになります。
"maxIdle" : 1800
-
説明2
"wicket:Key
で設定されているセッション情報は、Wicket を使用しているので、設定されているのかと思います。
BinaryDataで文字列として、そこそこ大きい情報が詰まれています。
Session に男らしく情報を詰め込んでいることがわかりました。
このところ、AP基盤系の設定ばかりしていますが、
基本的にどんなWebシステムでも、考慮が必要なものばかりです。
こういうところは、趣味?で作ってても仕事とつながりやすいところな気がします。
別につなげたいから、やっているわけではないですが..
追記 (2017/07/17)
Dropwizard 1.0.6 から 1.1.2 に update したところ、
jetty の version が 9.3 から、version 9.4 に上がり、MongoSessionIdManager
が削除されたり、
結構な変更が入っています。
Dropwizard 1.0.6 から 1.1.2 に update した話 | Monotalk
に変更方法を記載しましたので、そちらもご確認ください。
以上です。
コメント