Infinispan 9から、Spring用のモジュールにSpring Sessionのサポートが入りました。
Externalizing session using Spring Session
Embedded Mode、Client/Server Mode両方で使うことができます。
今回は、Embedded Modeで使ってみましょう。
また、Spring Boot向けのモジュールもリリースされているので、こちらも合わせて使ってみたいと思います。
Using Infinispan with Spring Boot
GitHub - infinispan/infinispan-spring-boot: Infinispan Spring Boot
Infinispan: Spring Boot Starters
準備
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>embedded-spring-session</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <scala-maven-plugin.version>3.2.2</scala-maven-plugin.version> <scala.version>2.12.2</scala.version> <infinispan.version>9.0.3.Final</infinispan.version> <infinispan-spring-boot.version>1.0.0.Final</infinispan-spring-boot.version> <spring-boot.version>1.5.4.RELEASE</spring-boot.version> <spring-session.version>1.3.1.RELEASE</spring-session.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-bom</artifactId> <version>${infinispan.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-spring-boot-starter</artifactId> <version>${infinispan-spring-boot.version}</version> </dependency> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-spring4-embedded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> <version>${spring-session.version}</version> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>${scala.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>${scala-maven-plugin.version}</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion>${scala.version}</scalaVersion> <args> <arg>-Xlint</arg> <arg>-unchecked</arg> <arg>-deprecation</arg> <arg>-feature</arg> </args> <recompileMode>incremental</recompileMode> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
主要なライブラリのバージョンは、BOMで指定することにしました。
<dependencyManagement> <dependencies> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-bom</artifactId> <version>${infinispan.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
BOMを使う場合は、Spring BootのものよりもInfinispanのものを先に書く必要があります。
でないと、Spring BootがInfinispan 8.2系を引っ張ってきてしまいます。
なお、今回のInfinispanは9.0.3.Finalを使用します。
あとは、InfinispanのSpring Boot StarterモジュールとSpring 4向けのEmbeddedのモジュール、
<dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-spring-boot-starter</artifactId> <version>${infinispan-spring-boot.version}</version> </dependency> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-spring4-embedded</artifactId> </dependency>
Spring BootのWebモジュールとSpring Sessionを足しておきます。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> <version>${spring-session.version}</version> </dependency>
これで、pom.xmlはおしまいです。
サンプルコード
それでは、Spring Sessionを使ったサンプルコードを書いていきます。今回は、セッション内にカウンター的なものを保存するようなものを
書いてみましょう。
Mainクラス。
src/main/scala/org/littlewings/infinispan/spring/App.scala
package org.littlewings.infinispan.spring import org.infinispan.spring.session.configuration.EnableInfinispanEmbeddedHttpSession import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication @EnableInfinispanEmbeddedHttpSession @SpringBootApplication class App object App { def main(args: Array[String]): Unit = { SpringApplication.run(classOf[App], args: _*) } }
ポイントは、@EnableInfinispanEmbeddedHttpSessionアノテーションを付与しておくことです。@EnableInfinispanEmbeddedHttpSessionでは、
Spring Sessionで使うInfinispanのCache名とセッションの有効期限を設定することができます。
デフォルトでは、Cacheの名前が「sessions」で、有効期限が30分です。
https://github.com/infinispan/infinispan/blob/9.0.3.Final/spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/configuration/EnableInfinispanEmbeddedHttpSession.java
今回は、デフォルトのまま使用します。
あとは、RestControllerとセッション間で共有するBeanを作成します。
src/main/scala/org/littlewings/infinispan/spring/CounterController.scala
package org.littlewings.infinispan.spring import java.time.LocalDateTime import java.time.format.DateTimeFormatter import org.springframework.stereotype.Component import org.springframework.web.bind.annotation.{GetMapping, RestController} import org.springframework.web.context.annotation.SessionScope import scala.collection.JavaConverters._ @RestController class CounterController(counter: Counter) { @GetMapping(Array("counter/access")) def access: java.util.Map[String, AnyRef] = { counter.increment() Map[String, AnyRef]( "value" -> Integer.valueOf(counter.value), "time" -> counter.time.format(DateTimeFormatter.ISO_DATE_TIME) ).asJava } } @SessionScope @Component @SerialVersionUID(1L) class Counter extends Serializable { var value: Int = 0 val time: LocalDateTime = LocalDateTime.now def increment(): Unit = value += 1 }
まあ、レスポンスはMapにしてるんですけど…。
設定ファイルも用意しましょう。Infinispanの設定ファイルは、このように準備。
src/main/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:9.0 http://www.infinispan.org/schemas/infinispan-config-9.0.xsd" xmlns="urn:infinispan:config:9.0"> <jgroups> <stack-file name="udp" path="default-configs/default-jgroups-udp.xml"/> </jgroups> <cache-container> <transport cluster="test-cluster" stack="udp"/> <distributed-cache name="sessions"/> </cache-container> </infinispan>
Spring Session用のCacheは、Distributed Cacheとしました。
この設定ファイルは、infinispan-spring-boot-starterを使用しているので、application.propertiesで指定することができます。
src/main/resources/application.properties
infinispan.embedded.configXml=infinispan.xml
これで、準備はおしまいです。
確認
では、パッケージングしてアプリケーションを起動してみましょう。
$ mvn package
Nodeは、3つとします。
## Node 1 $ java -jar target/embedded-spring-session-0.0.1-SNAPSHOT.jar ## Node 2 $ java -jar target/embedded-spring-session-0.0.1-SNAPSHOT.jar --server.port=8090 ## Node 3 $ java -jar target/embedded-spring-session-0.0.1-SNAPSHOT.jar --server.port=8100
クラスタが構成されます。
2017-06-24 18:23:17.668 INFO 20276 --- [xxxxx-50068] o.i.r.t.jgroups.JGroupsTransport : ISPN000094: Received new cluster view for channel test-cluster: [xxxxx-21343|2] (3) [xxxxx-21343, xxxxx-50068, xxxxx-23324]
curlで各Nodeにアクセスして確認します。
## Node 1 $ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/counter/access HTTP/1.1 200 Set-Cookie: SESSION=a39ab7b7-0bac-4d8f-8d4e-c43d8d944bb7; Path=/; HttpOnly Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 24 Jun 2017 09:25:38 GMT {"value":1,"time":"2017-06-24T18:25:37.962"} ## Node 2 $ curl -c cookie.txt -b cookie.txt -i http://localhost:8090/counter/access HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 24 Jun 2017 09:25:44 GMT {"value":2,"time":"2017-06-24T18:25:37.962"} ## Node 3 $ curl -c cookie.txt -b cookie.txt -i http://localhost:8100/counter/access HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 24 Jun 2017 09:26:33 GMT {"value":3,"time":"2017-06-24T18:25:37.962"} ## Node 1 $ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/counter/access HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 24 Jun 2017 09:26:44 GMT {"value":4,"time":"2017-06-24T18:25:37.962"} ## Node 2 $ curl -c cookie.txt -b cookie.txt -i http://localhost:8090/counter/access HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 24 Jun 2017 09:26:57 GMT {"value":5,"time":"2017-06-24T18:25:37.962"} ## Node 3 $ curl -c cookie.txt -b cookie.txt -i http://localhost:8100/counter/access HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 24 Jun 2017 09:27:05 GMT {"value":6,"time":"2017-06-24T18:25:37.962"}
Bean作成時に生成された時間も変わっていませんし、インクリメントした値も共有されてそうなのでOKですね。
まとめ
InfinispanのSpring Session向けのモジュールを、Embedded ModeでかつSpring Boot向けのStarterと一緒に試してみました。
Infinispan以外を使う時と同じように、簡単に使える感じなのでいいですね。
今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/infinispan-getting-started/tree/master/embedded-spring-session