先日、Infinispan 7がリリースされましたね。
Infinispan
http://infinispan.org/
Infinispan 7.0.0.Final is out!!
http://blog.infinispan.org/2014/11/infinispan-700final-is-out.html
個人的にけっこう見ているライブラリなのですが、バージョン7から設定ファイルの形式が変わったり、いろいろ変化があるようなので、これを機にちょっとある程度復習的に勉強しなおそうかと思いまして。
Infinispanって?
というわけで、イントロダクション的な。
Infinispanは、JBoss系のプロジェクトで「インメモリ・データグリッド」と呼ばれる分散データストアのカテゴリに属するものです。JBoss AS/WildFlyでのセッションレプリケーションなどにも使われているようですが、単体(アプリケーション内への組み込み、独立したサーバ形態)でも利用可能です。
また、分散構成前提というわけでもなく、ローカルキャッシュとして構成することもできます。
今年に決まったJSR-107(JCACHE)の実装のひとつです。
Getting Started的な
では、まずは使ってみます。
あ、基本的にうちのブログエントリでInfinispanを扱う時は、大半のケースでScalaを使用しているのであしからず…。
依存関係の定義など。
build.sbt
name := "embedded-local-starter" version := "0.0.1-SNAPSHOT" scalaVersion := "2.11.2" organization := "org.littlewings" scalacOptions ++= Seq("-Xlint", "-deprecation", "-unchecked", "-feature") incOptions := incOptions.value.withNameHashing(true) fork in Test := true parallelExecution in Test := false libraryDependencies ++= Seq( "org.infinispan" % "infinispan-core" % "7.0.0.Final", "net.jcip" % "jcip-annotations" % "1.0" % "provided", "org.scalatest" %% "scalatest" % "2.2.2" % "test" )
sbtでInfinispanを使う場合は、以前は依存関係の定義にだいぶてこずっていましたが、今は簡単になったようです。なお、ScalaでInfinispanを扱う時は、Infinispanに使われているアノテーションとScalaのコンパイラの関係で、jcip-annotationsは依存関係に含めておいた方がよいです。
今回、クラスタリングやサーバ構成はいったん置いておいて、Embeddedなローカルキャッシュとして使用してみます。
src/test/scala/org/littlewings/infinispan/embedded/EmbeddedCacheSpec.scala
package org.littlewings.infinsipan.embedded import java.util.concurrent.TimeUnit import org.infinispan.configuration.cache.ConfigurationBuilder import org.infinispan.manager.DefaultCacheManager import org.infinispan.transaction.{ LockingMode, TransactionMode } import org.infinispan.util.concurrent.IsolationLevel import org.scalatest.FunSpec import org.scalatest.Matchers._ class EmbeddedCacheSpec extends FunSpec { describe("Infinispan Embedded Cache, Gettings Started") { // ここに、テストコードを書く! } }
もっとも簡単な例。
it("non configuration Cache") { val manager = new DefaultCacheManager val cache = manager.getCache[String, String] cache.put("key1", "value1") cache.put("key2", "value2") cache.get("key1") should be ("value1") cache.get("key2") should be ("value2") cache.get("not-exists-key") should be (null) cache.remove("key1") cache.remove("key2") cache.get("key1") should be (null) cache.get("key2") should be (null) cache.stop() manager.stop() }
起点はDefaultCacheManagerで、getCacheメソッドで取得できるCacheのインスタンスを使用して、キャッシュ管理を行います。Cacheインターフェースはjava.util.Mapインターフェースの拡張でもあるので、操作方法はほぼMapになります。それなりに直感的に扱えるでしょう。
Cacheを取得する時に、名前を付けて取得することもできます。
it("using Named Cache, non configuration") { val manager = new DefaultCacheManager val cache = manager.getCache[String, String]("namedCache") cache.put("key1", "value1") cache.get("key1") should be ("value1") cache.getName should be ("namedCache") cache.stop() manager.stop() }
Cacheの管理は、名前ごとに別になります。
続いて、少しCacheの設定をしてみます。まずは、APIを使用して定義する場合。
it("confirm expiration, define programattically") { val manager = new DefaultCacheManager manager.defineConfiguration("withExpireCache", new ConfigurationBuilder() .expiration .maxIdle(3000L) .lifespan(5000L) .build) val cache = manager.getCache[String, String]("withExpireCache") cache.put("key1", "value1") cache.put("key2", "value2") TimeUnit.SECONDS.sleep(2) cache.get("key1") should be ("value1") TimeUnit.SECONDS.sleep(2) // maxIdle cache.get("key1") should be ("value1") cache.get("key2") should be (null) TimeUnit.SECONDS.sleep(2) // lifespan cache.get("key1") should be (null) cache.get("key2") should be (null) cache.stop() manager.stop() }
今回は、有効期限(アイドル時間、TTL)の設定をしてみました。これは、3秒間アクセスがなければエントリはなくなり、エントリ自体の有効期限は5秒という設定です。
次に、設定ファイルでCacheを定義してみます。
src/test/resources/infinispan.xml
<?xml version="1.0" encoding="UTF-8"?> <infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:7.0 http://www.infinispan.org/schemas/infinispan-config-7.0.xsd" xmlns="urn:infinispan:config:7.0"> <!-- この中に、cache-containerの定義を書く! --> </infinispan>
最小構成としては、こんな感じです。
で、先ほどのAPIでCache定義した時と、同等の定義を書いてみます。
<cache-container name="withExpireCacheManager" default-cache="withExpireCache"> <local-cache name="withExpireCache"> <expiration lifespan="5000" max-idle="3000" /> </local-cache> <jmx /> </cache-container>
Infinispan 7から設定ファイルの書き方が大きく変わっていて、JBoss ASやWildFlyの設定ファイルに書かれていた時みたいな定義方法になったようです。
あ、jmxタグ入れてる…。
このあたりが、ちゃんと見直そうと思った理由のひとつだったり。
設定ファイルを読み込む場合は、DefaultCacheManagerのコンストラクタ引数にファイル名を与える形でOKです。
it("confirm expiration, define configuration-file") { val manager = new DefaultCacheManager("infinispan.xml") val cache = manager.getCache[String, String]("withExpireCache") cache.put("key1", "value1") cache.put("key2", "value2") TimeUnit.SECONDS.sleep(2) cache.get("key1") should be ("value1") TimeUnit.SECONDS.sleep(2) // maxIdle cache.get("key1") should be ("value1") cache.get("key2") should be (null) TimeUnit.SECONDS.sleep(2) // lifespan cache.get("key1") should be (null) cache.get("key2") should be (null) cache.stop() manager.stop() }
動作的には、先ほどと同じですね。
あと、設定ファイルを書いた時に
<cache-container name="withExpireCacheManager" default-cache="withExpireCache">
の「default-cache」属性がちょっと気になっていたのですが、以前のdefaultタグの代わりなんでしょうね、きっと。
ここで指定された名前のCacheが、デフォルトのCache定義としてテンプレート的に使用されるという形のようですし、この内容を他のCacheが継承・上書きすることができるという点も変わらなさそうです。
The name of the default cache is defined in the
1.1. Configuring Cache declarativelyelement of the XML configuration file, and additional caches can be configured using the , , or elements.
今回のドキュメントでは、前回よりもこのあたりの説明がないような気もしますが。
確認してみましょう。
Cacheの定義。
<cache-container name="simpleCacheManager" default-cache="defaultCache"> <local-cache name="defaultCache"> <expiration lifespan="5000" max-idle="3000" /> </local-cache> <local-cache name="transactionalCache"> <locking isolation="REPEATABLE_READ" /> <transaction mode="NON_XA" locking="OPTIMISTIC" /> </local-cache> </cache-container>
default-cacheで指定しているものにはExpirationを指定して、これとは別にTransactionalなCacheを定義しました。
確認。
it("define configuration cache") { val manager = new DefaultCacheManager("infinispan.xml") val defaultCache = manager.getCache[String, String] val transactionalCache = manager.getCache[String, String]("transactionalCache") val nonDefinedCache = manager.getCache[String, String]("nonDefinedCache") defaultCache.getCacheConfiguration.expiration.lifespan should be (5000) defaultCache.getCacheConfiguration.expiration.maxIdle should be (3000) // extends default cache transactionalCache.getCacheConfiguration.expiration.lifespan should be (5000) transactionalCache.getCacheConfiguration.expiration.maxIdle should be (3000) // specified configuration transactionalCache .getCacheConfiguration .locking .isolationLevel should be (IsolationLevel.REPEATABLE_READ) transactionalCache .getCacheConfiguration .transaction .transactionMode should be (TransactionMode.TRANSACTIONAL) transactionalCache .getCacheConfiguration .transaction .lockingMode should be (LockingMode.OPTIMISTIC) nonDefinedCache.getCacheConfiguration.expiration.lifespan should be (5000) nonDefinedCache.getCacheConfiguration.expiration.maxIdle should be (3000) defaultCache.stop() transactionalCache.stop() nonDefinedCache.stop() manager.stop() }
名前を指定していないCacheと、定義していない名前のCacheはdefault-cacheの設定内容が入っていますし、Transactionalにした方のCacheもExpirationの設定が入っています。
この挙動、実は忘れていたり…。
ちょっと最近は時間の確保が難しくなってきているのですが、これからも少しずつ見ていこうと思います。
今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/infinispan-getting-started/tree/master/embedded-local-starter
GitHubのサンプル置き場も、新しくしましたー。