CLOVER🍀

That was when it all began.

Payara MicroでHazelcastのDiscovery SPIを使う

最近、HazelcastのDiscovery SPIでちょっと遊んでみました。

HazelcastのDiscovery SPIを試して解説する - CLOVER

今度はこのDiscovery SPIを、Payara Microで使ってみようかなと思いまして。

Discovery SPIは、Hazelcastのクラスタに参加するNode探す機能(Node Discovery)を実装するためのAPIセットでした。こちらを使うことで、マルチキャスト
使えない環境であっても、TCPでのホスト列挙設定以外の方法でHazelcastクラスタを構成することができるようになります。

ポイント

Payara MicroでHazelcastのDiscovery SPIを使うにあたって、ちょっと気をつけなくてはいけないことがあります。

それは、HazelcastのDiscovery SPIの実装を、デプロイするアプリケーションには含めないということです。

  • Hazelcastは、Payara Microにより使われる
  • Discovery SPIは、Service Providerの仕組みによってロードされる

このため、デプロイするアプリケーションにDiscovery SPIの実装を含めるのではなく、Payara Micro側に含めてあげる必要があります。ポイントは、ここになります。

で、どうすればいいのかというと、Payara Microには「--addjars」というオプションがあり、こちらを使うことでPayara Micro側のクラスパスにライブラリを追加することが
できるようになります。

Adding Third-Party JARs to a Micro Instance

こちらを活用してみましょう。

お題

こんなお題で、確認してみます。

環境

動作環境は、こんな感じ。

$ java -version
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

$ mvn -v
Apache Maven 3.5.3 (3383c37e1f9e9b3bc3df5050c29c8aff9f295297; 2018-02-25T04:49:05+09:00)
Maven home: /usr/local/maven3/current
Java version: 1.8.0_151, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-8-openjdk-amd64/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.4.0-104-generic", arch: "amd64", family: "unix"

使用するPayara Microは4.1.2.181、あとApache ZooKeeperは起動済みで3.4.11を使うものとします。

準備

作成するアプリケーションのMaven設定。

    <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>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.hazelcast</groupId>
            <artifactId>hazelcast</artifactId>
            <version>3.8.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

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

packagingは、warとしています。

アプリケーションの作成

簡単な、カウンタを作ってみます。

JAX-RSの有効化。
src/main/java/org/littlewings/payara/rest/JaxrsActivator.java

package org.littlewings.payara.rest;

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

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

HazelcastのAtomicLongを使った、カウンタを扱うJAX-RSリソースクラス。
src/main/java/org/littlewings/payara/rest/CalcResource.java

package org.littlewings.payara.rest;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IAtomicLong;

@ApplicationScoped
@Path("calc")
public class CalcResource {
    @Inject
    HazelcastInstance hazelcast;

    @GET
    @Path("inc")
    @Produces(MediaType.TEXT_PLAIN)
    public String increment() {
        IAtomicLong counter = hazelcast.getAtomicLong("counter");
        return "count = " + counter.incrementAndGet();
    }
}

とても単純です。

パッケージングして

$ mvn package

とりあえず、動かしてみましょう。

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

確認。

$ curl localhost:8080/app/rest/calc/inc
count = 1
count = 2
count = 3

OKそうです。クラスタ構成の確認は、はしょります。

Payara MicroでHazelcast Discovery Plugin for Apache ZooKeeperを使えるようにする

では、HazelcastのNode DiscoveryをApache ZooKeeperで行うようにしてみましょう。

まずは、Hazelcast Discovery Plugin for Apache ZooKeeper(以降、hazelcast-zookeeper)が依存するライブラリを、Payara Microに突っ込む必要があります。

ここは、Maven Dependency Pluginを使ってライブラリを集めましょう。まずは、hazelcast-zookeeperのpom.xmlを取得します。

$ wget 'https://search.maven.org/remotecontent?filepath=com/hazelcast/hazelcast-zookeeper/3.6.3/hazelcast-zookeeper-3.6.3.pom' -O hazelcast-zookeeper-3.6.3.pom

今回使用するhazelcast-zookeeperのバージョンは、3.6.3です。

hazelcast-zookeeperの依存ライブラリのうち、Hazelcastを除いた「compile」スコープのライブラリを「dependency:copy-dependencies」で集め、それから
hazelcast-zookeeperも忘れずに取得します。
*HazelcastはPayara Micro側に含まれているので、外しておきます

収集するディレクトリは、「zookeeper-discovery-spi-lib」とします。

$ mvn -f hazelcast-zookeeper-3.6.3.pom dependency:copy-dependencies -DoutputDirectory=zookeeper-discovery-spi-lib -DincludeScope=compile -DexcludeArtifactIds=hazelcast
$ wget 'https://search.maven.org/remotecontent?filepath=com/hazelcast/hazelcast-zookeeper/3.6.3/hazelcast-zookeeper-3.6.3.jar' -O zookeeper-discovery-spi-lib/hazelcast-zookeeper-3.6.3.jar

集めたライブラリ。

$ ls -l zookeeper-discovery-spi-lib
合計 6320
-rw-rw-r-- 1 xxxxx xxxxx   71909  318 23:30 curator-client-2.9.0.jar
-rw-rw-r-- 1 xxxxx xxxxx  190334  318 23:30 curator-framework-2.9.0.jar
-rw-rw-r-- 1 xxxxx xxxxx  273666  318 23:30 curator-recipes-2.9.0.jar
-rw-rw-r-- 1 xxxxx xxxxx   58325  318 23:30 curator-x-discovery-2.9.0.jar
-rw-rw-r-- 1 xxxxx xxxxx 2228009  318 23:30 guava-16.0.1.jar
-rw-rw-r-- 1 xxxxx xxxxx   11862  318 23:30 hazelcast-zookeeper-3.6.3.jar
-rw-rw-r-- 1 xxxxx xxxxx  232248  318 23:30 jackson-core-asl-1.9.13.jar
-rw-rw-r-- 1 xxxxx xxxxx  780664  318 23:30 jackson-mapper-asl-1.9.13.jar
-rw-rw-r-- 1 xxxxx xxxxx   87325  318 23:30 jline-0.9.94.jar
-rw-rw-r-- 1 xxxxx xxxxx  481535  318 23:30 log4j-1.2.16.jar
-rw-rw-r-- 1 xxxxx xxxxx 1208356  318 23:30 netty-3.7.0.Final.jar
-rw-rw-r-- 1 xxxxx xxxxx   32127  318 23:30 slf4j-api-1.7.12.jar
-rw-rw-r-- 1 xxxxx xxxxx  792964  318 23:30 zookeeper-3.4.6.jar

続いて、Apache ZooKeeperを使ってNode Discoveryを行うように、Hazelcastの設定を行います。こういう設定ファイルを用意。
hazelcast.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.8.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <properties>
        <property name="hazelcast.discovery.enabled">true</property>
    </properties>

    <group>
        <name>discovery-development</name>
        <password>D3v3l0pm3nt</password>
    </group>

    <network>
        <port>5900</port>
        <join>
            <multicast enabled="false"/>

            <discovery-strategies>
                <discovery-strategy enabled="true" class="com.hazelcast.zookeeper.ZookeeperDiscoveryStrategy">
                    <properties>
                        <property name="zookeeper_url">172.17.0.2:2181</property>
                        <!--
                        <property name="zookeeper_path">/discovery/hazelcast</property>
                        <property name="group">hazelcast</property>
                        -->
                    </properties>
                </discovery-strategy>
            </discovery-strategies>
        </join>
    </network>
</hazelcast>

Payara MicroのデフォルトのHazelcastの設定から、少し変更しました(groupの名前あたり)。

Payara Microで使用されるデフォルトのHazelcastの設定については、こちらを見るとよいでしょう。
https://github.com/payara/Payara/blob/payara-server-4.1.2.181/nucleus/payara-modules/hazelcast-bootstrap/src/main/java/fish/payara/nucleus/hazelcast/HazelcastRuntimeConfiguration.java

hazelcast-zookeeperの設定ですが、接続先以外はデフォルトのままです。

                <discovery-strategy enabled="true" class="com.hazelcast.zookeeper.ZookeeperDiscoveryStrategy">
                    <properties>
                        <property name="zookeeper_url">172.17.0.2:2181</property>
                        <!--
                        <property name="zookeeper_path">/discovery/hazelcast</property>
                        <property name="group">hazelcast</property>
                        -->
                    </properties>
                </discovery-strategy>

コメントアウトしている箇所は、デフォルト値を指していますが、Apache ZooKeeper上のパスを指します。
Apache ZooKeeper上には、「${zookeeper_path}/${group}」でノードが作られるようです。

なお、接続先URLのデフォルト値は「127.0.0.1:2181」です。

では、起動してみましょう。

ひとつ目のNodeを起動してみます。「--addjars」でhazelcast-zookeeperと関連ライブラリを含めたディレクトリを、「--hzconfigfile」で作成したHazelcastの
設定ファイルを指定しているところがポイントです。

$ java -jar payara-micro-4.1.2.181.jar --deploy target/app.war --addjars zookeeper-discovery-spi-lib --hzconfigfile hazelcast.xml

Node起動時に、Apache ZooKeeper側にはこういうログが出力されます。

2018-03-18 14:16:50,396 [myid:] - INFO  [SyncThread:0:ZooKeeperServer@683] - Established session 0x10001d290a00000 with negotiated timeout 40000 for client /172.17.0.1:54112
2018-03-18 14:16:51,280 [myid:] - INFO  [ProcessThread(sid:0 cport:2181)::PrepRequestProcessor@653] - Got user-level KeeperException when processing sessionid:0x10001d290a00000 type:create cxid:0x1 zxid:0x2 txntype:-1 reqpath:n/a Error Path:/discovery/hazelcast/hazelcast Error:KeeperErrorCode = NoNode for /discovery/hazelcast/hazelcast

「/discovery/hazelcast」が「zookeeper_path」のデフォルト値、「hazelcast」が「group」のデフォルト値でした。

Path:/discovery/hazelcast/hazelcast Error:KeeperErrorCode = NoNode for /discovery/hazelcast/hazelcast

続いて、2つ目のNodeを起動します。HTTPリッスンポートは、8081を使うことにしました。

$ java -jar payara-micro-4.1.2.181.jar --deploy target/app.war --port 8081 --addjars zookeeper-discovery-spi-lib --hzconfigfile hazelcast.xml 

Apache ZooKeeper側にも接続されたというログが現れ

2018-03-18 14:20:25,820 [myid:] - INFO  [SyncThread:0:ZooKeeperServer@683] - Established session 0x10001d290a00001 with negotiated timeout 40000 for client /172.17.0.1:54144

Hazelcastとしてもクラスタが構成されます。

[2018-03-18T23:20:33.087+0900] [] [情報] [] [com.hazelcast.internal.cluster.ClusterService] [tid: _ThreadID=56 _ThreadName=hz._hzInstance_1_discovery-development.generic-operation.thread-0] [timeMillis: 1521382833087] [levelValue: 800] [[
  [172.17.0.1]:5900 [discovery-development] [3.8.5] 

Members [2] {
	Member [172.17.0.1]:5900 - dfc320fd-1417-4834-bc3d-dfba949caf06 this
	Member [172.17.0.1]:5901 - 38118a41-08e7-4094-bd0f-33d70021a3f0
}
]]

確認してみましょう。

$ curl localhost:8080/app/rest/calc/inc
count = 1

$ curl localhost:8081/app/rest/calc/inc
count = 2

$ curl localhost:8080/app/rest/calc/inc
count = 3

$ curl localhost:8081/app/rest/calc/inc
count = 4

OKそうですね。

オマケ

Uber JARにする場合は、こんな感じで。

$ java -jar payara-micro-4.1.2.181.jar --deploy target/app.war --addjars zookeeper-discovery-spi-lib --outputuberjar target/app.jar

起動。

$ java -jar target/app.jar --hzconfigfile hazelcast.xml

Payara Micro Maven Pluginを使ってUber JARを使おうかとも思ったのですが、customJars/artifactItemでは依存関係を含めた形でJARに含めてくれない
みたいなので、今回はパスで…。

まとめ

HazelcastのDiscovery SPIを使って、Payara Microのクラスタを構成してみました。

ちょっと一手間あるものの、これでDiscovery SPIを使ってのクラスタ構成が可能なようですね。ポイントは「--addjars」でしたが、このオプションを覚えて
おくとしましょう。