CLOVER🍀

That was when it all began.

Payara MicroでHazelcastの設定をする

Payara Microで、「--hzConfigFile」という起動オプションを使用することで、Hazelcastの設定ファイルを与えることができるようです。

なんとなくオプションが追加されていたのは気付いていましたが、これが使えるようになったのはPayara 4.1.153からっぽいですね。

Payara Micro Command Line Options

Payara 4.1.153以前はPayara MicroではHazelcastの設定に付いては、マルチキャストアドレス/ポート、コミュニケーションポートの開始ポート、そもそもクラスタを無効化するくらいが指定できたようですが、設定ファイルが使えるとだいぶ設定の幅が広がりますね。

せっかくなので、ちょっと試してみました。

準備

まずは、動作対象となるアプリケーションを作ってみます。

pom.xmlは、こんな感じに。
pom.xml

<?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>payara-micro-hazelcast-configuration</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <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>

    <build>
        <finalName>app</finalName>
    </build>

    <dependencies>
        <dependency>
            <groupId>fish.payara.extras</groupId>
            <artifactId>payara-micro</artifactId>
            <version>4.1.1.162</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

アプリケーションとしては、HttpSessionを使った簡単なJAX-RS APIを用意します。
src/main/java/org/littlewings/payara/rest/JaxrsApplication.java

package org.littlewings.payara.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("rest")
public class JaxrsApplication extends Application {
}

src/main/java/org/littlewings/payara/rest/HelloWorldResource.java

package org.littlewings.payara.rest;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Path("helloworld")
public class HelloWorldResource {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String helloWorld(@Context HttpServletRequest request) {
        HttpSession session = request.getSession();

        String time = (String) session.getAttribute("time");
        if (time == null) {
            LocalDateTime now = LocalDateTime.now();
            time = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            session.setAttribute("time", time);
        }

        return time;
    }
}

web.xmlには、distributableを付けておきます。
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>

で、アプリケーションをビルドしておきます。

$ mvn package

以降、このアプリケーションを起動する時に、Payara MicroにデプロイしつつHazelcastの設定を変えていってみます。

今回利用するPayara MicroのJARファイルは、payara-micro-4.1.1.162.jarとなります。

Payara Server & Payara Micro - Downloads

また、今回はPayara Microで起動するアプリケーションは、Node 2つでクラスタを組むものとします。2つ目のNodeのHTTPリッスンポートは、9080とします。

起動時には、以下のコマンドで実行することを前提とします。以降では、以下のコマンドの[Hazelcastの設定ファイル]の部分に該当するファイルを作成、指定して実行していきます。

## Node 1
$ java -jar payara-micro-4.1.1.162.jar --deploy target/app.war --hzConfigFile [Hazelcastの設定ファイル]

## Node 2
$ java -jar ../payara-micro-4.1.1.162.jar --deploy target/app.war --port 9080 --hzConfigFile [Hazelcastの設定ファイル]

HttpSessionが共有されているかどうかの動作確認には、以下のコマンドを使っています。

## Node 1
$ curl -b cookie.txt -c cookie.txt http://localhost:8080/app/rest/helloworld
2016-06-18 21:18:28

## Node 2
$ curl -b cookie.txt -c cookie.txt http://localhost:9080/app/rest/helloworld
2016-06-18 21:18:28

HttpSessionが共有されていれば、同じ日時が返ってきます、と。

グループ名を変えてみる

まず、設定ファイルの内容が反映されているか確認するために、グループ名を変えてみることにします。ここでいうグループ名とは、Hazelcastがクラスタを構成するためのグループで、Hazelcastクラスタはひとつのグループに属することになります。

Creating Cluster Groups

最初は、設定なしの状態で起動してみましょう。

$ java -jar payara-micro-4.1.1.162.jar --deploy target/app.war

起動時に、Hazelcastクラスタに属するメンバーが以下のように出力されます。

[2016-06-18T20:27:17.624+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.cluster.impl.MulticastJoiner] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1466249237624] [levelValue: 800] [[
  [172.17.0.1]:5900 [development] [3.6.2] 


Members [1] {
	Member [172.17.0.1]:5900 this
}
]]

ここで、次のような設定ファイルを用意します。groupタグの中が、グループの設定です。クラスタグループ名は、「my-cluster」にしました。
注)マルチキャスト有効化の設定が入っていますが、これを書かないとクラスタ構成自体も無効化されてしまうようなので、設定ファイルを与える場合は明示する必要があるようです
hazelcast-custom-group.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <group>
        <name>my-cluster</name>
        <password>password</password>
    </group>

    <network>
        <join>
            <multicast enabled="true"/>
        </join>
    </network>
</hazelcast>

では、起動してみます。

$ java -jar ../payara-micro-4.1.1.162.jar --deploy target/app.war --hzConfigFile hazelcast-custom-group.xml

起動時に認識したメンバーの出力が、以下のようになりました。

[2016-06-18T20:34:14.325+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.cluster.impl.MulticastJoiner] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1466249654325] [levelValue: 800] [[
  [172.17.0.1]:5701 [my-cluster] [3.6.2] 


Members [1] {
	Member [172.17.0.1]:5701 this
}
]]

※コミュニケーションポートの設定がHazelcastのデフォルトに戻っているので、5701に…

ちゃんと「my-cluster」になっていますね。

  [172.17.0.1]:5701 [my-cluster] [3.6.2] 

続いて、他の設定も変えていってみましょう。

マルチキャストを無効化する

(デフォルトの)Hazelcastはマルチキャストを使ってクラスタを構成しますが(Node Discovery)、開発環境等でこれを無効化するには、ネットワーク設定でマルチキャストを使わないようにします。

Join

hazelcast-disable-mc.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <network>
        <join>
            <multicast enabled="false"/>
        </join>
    </network>
</hazelcast>

これで、スタンドアロンになります。

まあ、Payara Microの場合はHazelcastの設定ファイルを与えてしまうと、マルチキャストを明示的に有効化しないとスタンドアロンになるみたいですが…。

ちなみに、単にクラスタを無効化したいのであれば、Hazelcastの設定ファイルを与えずとも「--noCluster」を使えばOKです。起動もちょこっと速くなりますね。

$ java -jar ../payara-micro-4.1.1.162.jar --deploy target/app.war --noCluster

この方法でもクラスタは構成しなくなります。

TCPクラスタを構成する

Payara Micro+Hazelcastのデフォルトのクラスタ構成(Node Discovery)はマルチキャストですが、これをTCPに変更してみます。

Join

設定ファイルの例は、以下のとおり。
hazelcast-enable-tcp.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <network>
        <port auto-increment="true">6000</port>
        <join>
            <multicast enabled="false"/>
            <tcp-ip enabled="true">
                <member>localhost</member>
            </tcp-ip>
        </join>
    </network>
</hazelcast>

今回はわかりやすいように、コミュニケーションポートも6000に変えてみました(Payara Microのデフォルトだと5900)。また、localhostの範囲でクラスタを作るようにしています。

これで、TCPでHazelcastクラスタが構成されます。

[2016-06-18T20:43:26.613+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.cluster.ClusterService] [tid: _ThreadID=47 _ThreadName=hz._hzInstance_1_dev.generic-operation.thread-0] [timeMillis: 1466250206613] [levelValue: 800] [[
  [localhost]:6000 [dev] [3.6.2] 

Members [2] {
	Member [localhost]:6000 this
	Member [localhost]:6001
}
]]

Mapの設定をしてみる

続いて、Payara MicroがHttpSessionのデータ保存に利用する、Distributed Mapの設定をしてみましょう。

Map

今回は、Distributed Map名「/app」に対して、いろいろ設定してみました。
hazelcast-custom-map.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <network>
        <join>
            <multicast enabled="true"/>
        </join>
    </network>

    <map name="/app">
        <in-memory-format>BINARY</in-memory-format>
        <backup-count>0</backup-count>
        <async-backup-count>1</async-backup-count>

        <near-cache name="default">
            <in-memory-format>BINARY</in-memory-format>
            <max-size>100</max-size>
            <time-to-live-seconds>600</time-to-live-seconds>
            <max-idle-seconds>60</max-idle-seconds>
            <invalidate-on-change>true</invalidate-on-change>
            <cache-local-entries>false</cache-local-entries>
        </near-cache>
    </map>
</hazelcast>

ムダにNear Cacheを使ってみたり、バックアップは非同期で作成するような設定にしています。

なんで名前が「/app」かなのですが、WARファイル名前というか、コンテキストパスに依存しています?

今回のWARファイルの名前は、「app.war」です。
※pom.xmlより

    <build>
        <finalName>app</finalName>
    </build>

なお、「default」という特殊な名前のDistributed Mapの設定を行うと、他に作成されるDistributed Mapにも設定が引き継がれるので注意しておきましょう。

    <map name="default">

明示的に名前を付けて設定を定義した場合は、「default」には引きずられないようです。

Hazelcastの設定をしてみよう - CLOVER

Mananagement Centerを使う

最後は、Hazelcastが提供するManagement Centerと接続するように設定してみましょう。

Management Center

このManagement Centerを使うと、Hazelcastクラスタのモニタリングができるようになります。
※Payara自体がどの程度Hazelcastの状態を見れるかはわかっていません、スミマセン…

Management Centerを使うには、まずHazelcastのダウンロードページよりHazelcastのディストリビューションをダウンロードしてくる必要があります。

Hazelcast IMDG Downloads

今回は、Payaraが依存しているHazelcast 3.6.2のtar.gzファイルをダウンロードしました。

展開。

$ tar -zxvf hazelcast-3.6.2.tar.gz

ll hazelcast-3.6.2/mancenter
合計 34924
drwxr-xr-x 2 xxxxx xxxxx     4096  618 19:51 ./
drwxr-xr-x 9 xxxxx xxxxx     4096  618 19:51 ../
-rw-r--r-- 1 xxxxx xxxxx 35743282  618 19:51 mancenter-3.6.2.war
-rw-r--r-- 1 xxxxx xxxxx      453  618 19:51 startManCenter.bat
-rw-r--r-- 1 xxxxx xxxxx      306  618 19:51 startManCenter.sh

このManagement Center、単独でも実行可能WARとして起動できるのですが、今回はこれもPayara Microにデプロイしてみます。

クラスタを構成する必要はないので、「--noCluster」を付与します。また、HTTPリッスンポートは20000とします。

$ java -jar payara-micro-4.1.1.162.jar --deploy hazelcast-3.6.2/mancenter/mancenter-3.6.2.war --noCluster --port 20000

この設定で、「http://localhost:20000/mancenter-3.6.2」にアクセスすると以下のような画面が表示されます。

デフォルトだと、そのまま「login」でログインできます。

まだクラスタから情報が届いていないので、Hazelcastクラスタに関する情報はありません。

それでは、アプリケーション側のHazelcastの設定を行います。
hazelcast-mancenter.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <network>
        <join>
            <multicast enabled="true"/>
        </join>
    </network>

    <management-center enabled="true" update-interval="3">http://localhost:20000/mancenter-3.6.2</management-center>
</hazelcast>

「management-center」タグを使うことで、Management Centerへの接続設定を行うことができます。

今回は、「http://localhost:20000/mancenter-3.6.2」へ3秒に1度情報を更新する設定になっています。

Distributed Mapなどの設定は、デフォルトです。

ここで、2 Node起動してみます。すると、Management Centerの方でHazelcastクラスタを検知しているのでManagement Centerの開いているブラウザをリロードします。

ここで、このまま「Connect」ボタンを押すと、Hazelcastクラスタの情報が参照できます。

「/app」というMapsへのリンクがあるので、こちらを見てみます。

その後でcurlでアプリケーションに対してアクセスしてみます。

## to Node 1
curl -b cookie.txt -c cookie.txt http://localhost:8080/app/rest/helloworld

## to Node 2
curl -b cookie.txt -c cookie.txt http://localhost:9080/app/rest/helloworld

すると、「/app」Map側に情報が反映されます。

とまあ、こんな感じにPayara Microが使っているHttpSession(Distributed Map)の情報が見れました、と。

まとめ

Payara Microで起動するアプリケーションに対して、Hazelcastの設定ファイルを与えていくつか挙動を変えてみました。

Management CenterなどはちょっとHazelcastの製品寄りの話な気もしますが…あと、Payara自体の管理機能は見ていませんが…。

まあ、Payara Microを使ってもある程度Hazelcastの設定を制御できそうなことがわかってよかったです。