OSCacheのクラスタリングは今までやったことがなかったのですが、わけあってやってみました。
まず、OSCacheのクラスタリング機能が提供してくれる機能についてですが、やってくれるのはflushの同期のみです。キャッシュエントリそのもののレプリケーションをしてくれるわけではありません。レプリケーションのコストを考えると当然だとは思いますが、利用にあたってはこの点は意識しておく必要があります。
というわけで、要するにOSCacheのクラスタリング機能というのはクラスタに参加しているノードに対してflushメッセージを伝達するだけのもの、と考えるとよいです。で、そのメッセージの伝達方法にはJMSのトピックを使う方法と、JGroupsを使う方法が用意されています。
Tomcatとか使ってるのにJMSサーバを別に立てるのはめんどくさいので、今回は簡単に利用できるJGroupsを使う方法を試してみました。
設定
設定は簡単で、OSCacheのJARファイルに加えてjgroups-all.jarをクラスパスに追加したうえでoscache.propertiesに以下の一行を追加するだけ。
cache.event.listeners=com.opensymphony.oscache.plugins.clustersupport.JavaGroupsBroadcastingListener
これでOKと思いきや、OSCache 2.4.1に含まれているJGroups 2.2.8には問題があるらしく、実行するとなんかエラーが出ます。JGroupsのWebサイトから最新のJARを取ってきて入れ替えたら動きました。
ちなみにJGroupsの詳細な設定を行いたい場合はoscache.propertiesのcache.cluster.propertiesという項目を使うみたいです。デフォルトでは以下のような設定になっているそうな。
cache.cluster.properties=UDP(mcast_addr=231.12.21.132;mcast_port=45566;ip_ttl=32;\ mcast_send_buf_size=150000;mcast_recv_buf_size=80000):\ PING(timeout=2000;num_initial_members=3):\ MERGE2(min_interval=5000;max_interval=10000):\ FD_SOCK:VERIFY_SUSPECT(timeout=1500):\ pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800;max_xmit_size=8192):\ UNICAST(timeout=300,600,1200,2400):\ pbcast.STABLE(desired_avg_gossip=20000):\ FRAG(frag_size=8096;down_thread=false;up_thread=false):\ pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=true)
JavaVMの起動オプション
IPv6が利用可能な場合、IPv6が優先されてしまうようです。JavaVMの起動オプションに「-Djava.net.preferIPv4Stack=true」を追加することでIPv4を優先させることができます。
また、複数のネットワークアダプタが存在する場合、最初に見つかったものが使用されてしまうようです。この場合は「-Djgroups.bind_addr=
僕はホスト上のTomcatとVMWare上のTomcatでクラスタリングを試したので、ホスト側のTomcatを起動する際にNAT用のアダプタ(VMnet8)のIPアドレスを指定しました。
flushするときの注意点
これで一応動くはずですが、キャッシュをflushする際に1つ注意点があります。WebアプリケーションではServletCacheAdministratorというキャッシュ管理クラスを利用できるのですが、以下のようにServletCacheAdministratorのflushAll()メソッドを呼び出してもflushしたというメッセージの伝達が行われません。
ServletCacheAdministrator cacheAdmin
= ServletCacheAdministrator.getInstance(getServletContext());
// これはダメ
cacheAdmin.flushAll();
以下のように各スコープのCacheオブジェクトを取得し、Cache#flushAll()とかを呼べば、クラスタに参加しているノードに「ちゃんとflushしたよー」というメッセージが飛びます。
ServletCacheAdministrator cacheAdmin = ServletCacheAdministrator.getInstance(getServletContext()); // これはOK Cache cache = cacheAdmin.getAppScopeCache(getServletContext()); cache.flushAll(new Date());
なんというか、いちおう動くには動いたのですが、使いこなすには結構コツが必要な感じです。