Payara Microに同梱されたHazelcastを使って、HTTPセッションのレプリケーションができそうな感じだったので試してみました。
確認
<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>payara-micro-session-clustering</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <build> <finalName>payara-micro-session-clustering</finalName> </build> <dependencies> <dependency> <groupId>fish.payara.extras</groupId> <artifactId>payara-micro</artifactId> <version>4.1.152.1</version> <scope>provided</scope> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> </properties> </project>
Payara MicroのJARファイルは、以下のページからダウンロードしてください。
http://www.payara.co.uk/downloads
今回利用するのは、「payara-micro-4.1.152.1.jar」です。
確認用のサンプルを用意。JAX-RSで行うことにします。
JAX-RS有効化のためのクラス。
src/main/java/org/littlewings/hazelcast/rest/JaxrsApplication.java
package org.littlewings.hazelcast.rest; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("rest") public class JaxrsApplication extends Application { }
せっかくなので、自前でクラスを定義してHttpSessionに放り込むとしましょう。
src/main/java/org/littlewings/hazelcast/rest/User.java
package org.littlewings.hazelcast.rest; import java.io.Serializable; public class User implements Serializable { public String name; public int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } }
あんまり良くありませんが、簡単のため今回はJAX-RSリソースクラスでHttpSessionを直接扱います。
src/main/java/org/littlewings/hazelcast/rest/SessionResource.java
package org.littlewings.hazelcast.rest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; @Path("session") public class SessionResource { @GET @Path("put/{name}") @Produces(MediaType.APPLICATION_JSON) public User put(@PathParam("name") String name, @QueryParam("age") int age, @Context HttpServletRequest request) { User user = new User(name, age); request.getSession().setAttribute(name, user); return user; } @GET @Path("update/{name}") @Produces(MediaType.APPLICATION_JSON) public User update(@PathParam("name") String name, @QueryParam("age") int age, @Context HttpServletRequest request) { User user = (User) request.getSession().getAttribute(name); user.age = age; return user; } @GET @Path("get/{name}") @Produces(MediaType.APPLICATION_JSON) public User get(@PathParam("name") String name, @Context HttpServletRequest request) { HttpSession session = request.getSession(); return (User) session.getAttribute(name); } }
そして、HttpSessionのレプリケーションを行うためには、web.xmlが必要です。
src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <distributable/> </web-app>
distributable要素が必要ですよ、と。
では、パッケージングして
$ mvn package
起動。2つNodeを起動します。
## Node 1 $ java -jar payara-micro-4.1.152.1.jar --deploy target/payara-micro-session-clustering.war ## Node 2 $ java -jar payara-micro-4.1.152.1.jar --deploy target/payara-micro-session-clustering.war --port 8180
2つめのNodeは、リッスンポートを8180に設定。
起動時にクラスタが構成されることを確認しておきます。
Members [2] { Member [192.168.254.129]:5900 Member [192.168.254.129]:5901 this }
では、Cookieを保存しつつアクセスしてみます。
まずはデータ登録。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/payara-micro-session-clustering/rest/session/put/Taro?age=20 HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONID=0126279d6eb5e563971fcfb08dba; Path=/payara-micro-session-clustering; HttpOnly Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:0; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:27:49 GMT Content-Length: 24 {"name":"Taro","age":20}
同じNodeに対して、参照。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/payara-micro-session-clustering/rest/session/get/Taro HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:1; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:28:33 GMT Content-Length: 24 {"name":"Taro","age":20}
Node 2に対して、参照。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8180/payara-micro-session-clustering/rest/session/get/Taro HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:2; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:29:03 GMT Content-Length: 24 {"name":"Taro","age":20}
OKですね。
Node 2でデータ登録。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8180/payara-micro-session-clustering/rest/session/put/Hanako?age=22 HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:3; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:30:17 GMT Content-Length: 26 {"name":"Hanako","age":22}
Node 1で参照。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/payara-micro-session-clustering/rest/session/get/Hanako HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:4; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:30:43 GMT Content-Length: 26 {"name":"Hanako","age":22}
あとは、ちょっと微妙な使い方ですが、セッションに保存済みのインスタンスのメンバーを更新して、HttpSession#setAttributeを再度呼び出さなかった場合は?
@GET @Path("update/{name}") @Produces(MediaType.APPLICATION_JSON) public User update(@PathParam("name") String name, @QueryParam("age") int age, @Context HttpServletRequest request) { User user = (User) request.getSession().getAttribute(name); user.age = age; return user; }
Node 1に対して更新。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/payara-micro-session-clustering/rest/session/update/Taro?age=30 HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:5; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:33:34 GMT Content-Length: 24 {"name":"Taro","age":30}
Node 2で見てみます。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8180/payara-micro-session-clustering/rest/session/get/Taro HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f7061796172612d6d6963726f2d73657373696f6e2d636c7573746572696e67:6; Path=/payara-micro-session-clustering; HttpOnly Content-Type: application/json Date: Fri, 29 May 2015 14:34:00 GMT Content-Length: 24 {"name":"Taro","age":30}
反映されています…。ちょっと意外でした。
オマケ
Hazelcastには、実はServletFilterで実装されたHttpSessionレプリケーションの機能があります。
Web Session Replication
http://docs.hazelcast.org/docs/3.4/manual/html-single/hazelcast-documentation.html#web-session-replication
で、Payaraではどうしているかですが、自前でHazelcastを使ったレプリケーションを実装しているみたいです。
HazelcastのWMモジュール自体、Payaraの依存関係に入っていないようです。
既存のHAの仕組みに乗せた感じでしょうか?それなら、その方が良いと思いますが。
個人的には、これでHazelcast on Payara Microの気になるところはだいたい初歩的な感じで触ったつもりです。
今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/hazelcast-examples/tree/master/payara-micro-session-clustering