CLOVER🍀

That was when it all began.

Infinispan Serverのネイティブイメージを試す

これは、なにをしたくて書いたもの?

Infinispan 11.0.0.Finalから、Infinispan Serverのネイティブイメージがリリースされています。

Blog: Infinispan 11.0.0.Final - Infinispan

Blog: Infinispan Native Server Image - Infinispan

前々から気になっていたので、ちょっと遊んでみることにしました。

Infinispan Native Server

文字通り、Infinispan ServerをGraalVMを使ってビルドしたネイティブイメージです。通常のInfinispan Serverよりも
コンパクトなサイズになっています。

利用は、実行可能なネイティブイメージではなく、Dockerイメージとして利用することになります。

Container Image

Infinispan Server Native Image

GitHubリポジトリはこちらです。Infinispan Server向けの、Quarkus Extensionが実装されています。

GitHub - infinispan/infinispan-quarkus: Infinispan Quarkus based extensions and Server

Dockerイメージを作る元になっているリポジトリは、こちら。ネイティブイメージ以外のDockerイメージの元も含まれています。

GitHub - infinispan/infinispan-images: Infinispan is an open source data grid platform and highly scalable NoSQL cloud data store.

通常のInfinispan ServerのDockerイメージとのサイズを比較してみましょう。

$ docker image pull quay.io/infinispan/server-native:12.0.2.Final
$ docker image pull quay.io/infinispan/server:12.0.2.Final


$ docker image ls | grep quay.io/infinispan/server
quay.io/infinispan/server-native   12.0.2.Final           c0d39bf9da9a   2 weeks ago     280MB
quay.io/infinispan/server          12.0.2.Final           a85f09e76880   2 weeks ago     476MB

通常のInfinispan Serverのサイズから半減…とまではいきませんが、小さくなっていますね。

現在のInfinispanのドキュメントには、ネイティブイメージに関する情報はなさそうなので、GitHubやブログの内容を元に
見ていきたいと思います。

起動してみる

まずは起動してみましょう。Dockerイメージは、quay.io/infinispan/server-native:12.0.2.Finalを使います。12.0.2.Finalですね。

$ docker container run -it --rm --name infinispan quay.io/infinispan/server-native:12.0.2.Final

起動すると、こんなことを言われます。

################################################################################
#                                                                              #
# IDENTITIES_PATH not specified                                                #
# USER and/or PASS env variables not specified.                                #
# Auto generating user and password.                                           #
#                                                                              #
# Generated User: 8Wg5LOkbDw                                                   #
# Generated Password: RWPtynD6w0                                               #
#                                                                              #
# These credentials should be passed via environment variables when adding     #
# new nodes to the cluster to ensure that clients can access the exposed       #
# endpoints, on all nodes, using the same credentials.                         #
#                                                                              #
# For example:                                                                 #
#     'docker run -e USER=8Wg5LOkbDw -e PASS=RWPtynD6w0''                      #
#                                                                              #
################################################################################

ユーザーを自動生成した、と言っています。指定するには、環境変数で設定すればよさそうですね。

他のログ。

2021-03-27 15:49:09,165 WARN  [io.qua.qut.run.ContentTypes] (main) Unable to detect the content type for templates/jgroups-encrypt; using application/octet-stream
2021-03-27 15:49:09,166 INFO  [io.quarkus] (main) config-generator 2.1.3.Final native (powered by Quarkus 1.7.6.Final) started in 0.008s. 
2021-03-27 15:49:09,166 INFO  [io.quarkus] (main) Profile prod activated. 
2021-03-27 15:49:09,166 INFO  [io.quarkus] (main) Installed features: [cdi, qute]
2021-03-27 15:49:09,170 INFO  [io.quarkus] (main) config-generator stopped in 0.000s
2021-03-27 15:49:09,191 INFO  [ListenerBean] (main) The application is starting...
2021-03-27 15:49:09,206 INFO  [org.inf.CONTAINER] (main) ISPN000556: Starting user marshaller 'org.infinispan.commons.marshall.ImmutableProtoStreamMarshaller'
2021-03-27 15:49:09,263 INFO  [org.inf.CONTAINER] (main) ISPN000128: Infinispan version: Infinispan 'Lockdown' 12.0.2.Final
2021-03-27 15:49:09,264 INFO  [org.inf.CLUSTER] (main) ISPN000078: Starting JGroups channel infinispan with stack image-tcp
2021-03-27 15:49:11,267 INFO  [org.jgr.pro.pbc.GMS] (main) fec90b7741da-38815: no members discovered after 2000 ms: creating cluster as coordinator
2021-03-27 15:49:11,267 INFO  [org.inf.CLUSTER] (main) ISPN000094: Received new cluster view for channel infinispan: [fec90b7741da-38815|0] (1) [fec90b7741da-38815]
2021-03-27 15:49:11,268 INFO  [org.inf.CLUSTER] (main) ISPN000079: Channel infinispan local address is fec90b7741da-38815, physical addresses are [172.17.0.2:7800]
2021-03-27 15:49:11,269 INFO  [org.inf.CONTAINER] (main) ISPN000390: Persisted state, version=12.0.2.Final timestamp=2021-03-27T15:49:11.269025Z
2021-03-27 15:49:11,278 INFO  [org.inf.CONTAINER] (main) ISPN000104: Using EmbeddedTransactionManager
2021-03-27 15:49:11,305 INFO  [org.inf.SERVER] (main) ISPN080018: Started connector REST (internal)
2021-03-27 15:49:11,306 INFO  [org.inf.SERVER] (ForkJoinPool.commonPool-worker-3) ISPN080018: Started connector HotRod (internal)
2021-03-27 15:49:11,307 INFO  [org.inf.SERVER] (main) ISPN080004: Connector SINGLE_PORT (default) listening on 172.17.0.2:11222
2021-03-27 15:49:11,307 INFO  [org.inf.SERVER] (main) ISPN080034: Server 'fec90b7741da-38815' listening on http://172.17.0.2:11222
2021-03-27 15:49:11,307 INFO  [org.inf.SERVER] (main) ISPN080001: Infinispan Server 12.0.2.Final started in 2124ms
2021-03-27 15:49:11,308 INFO  [io.quarkus] (main) infinispan-quarkus-server-runner 12.0.2.Final native (powered by Quarkus 1.11.0.Final) started in 2.130s. 
2021-03-27 15:49:11,308 INFO  [io.quarkus] (main) Profile prod activated. 
2021-03-27 15:49:11,308 INFO  [io.quarkus] (main) Installed features: [cdi, infinispan-embedded, infinispan-server]

Infinispan Server 12.0.2.Final、Quarkusは1.11.0.Finalですね。

2021-03-27 15:49:11,307 INFO  [org.inf.SERVER] (main) ISPN080001: Infinispan Server 12.0.2.Final started in 2124ms
2021-03-27 15:49:11,308 INFO  [io.quarkus] (main) infinispan-quarkus-server-runner 12.0.2.Final native (powered by Quarkus 1.11.0.Final) started in 2.130s. 

1度止めまして。

中に含まれるファイルおよびディレクトリは、こんな感じです。

$ docker container run -it --rm --name infinispan quay.io/infinispan/server-native:12.0.2.Final find /opt/infinispan
/opt/infinispan
/opt/infinispan/bin
/opt/infinispan/bin/config-generator
/opt/infinispan/bin/server-runner
/opt/infinispan/bin/server.sh
/opt/infinispan/bin/launch.sh
/opt/infinispan/server
/opt/infinispan/server/conf
/opt/infinispan/server/conf/users.properties
/opt/infinispan/server/conf/groups.properties
/opt/infinispan/data

Infinispan Serverのネイティブイメージは、/opt/infinispanディレクトリ内にあります。

また、最初のコメントで表示されていたように、環境変数USERおよびPASSを指定することでユーザー名および
パスワードを設定できます。

$ docker container run -it --rm -e USER=ispn-user -e PASS=ispn-password --name infinispan quay.io/infinispan/server-native:12.0.2.Final

起動時のコメントが変わりました。

################################################################################
#                                                                              #
# IDENTITIES_PATH not specified                                                #
# Generating Identities yaml using USER and PASS env vars.                     #
################################################################################

このあたりの設定については、README.mdに書かれているのですが。

Infinispan Images / Server

Infinispan Images / Yaml Configuration

今回は、Infinispan ServerのDockerイメージを使い倒すことを目的はしていないので、あまり深追いしません。

Dockerイメージがどんな構成になっているかや、指定できる環境変数は以下のファイルを見ればよさそうです。

https://github.com/infinispan/infinispan-images/blob/server-native-12.0.2.Final-1/server-native.yaml

https://github.com/infinispan/infinispan-images/blob/server-native-12.0.2.Final-1/modules/org/infinispan/runtime/added/bin/launch.sh

ここから先は、まずはSingle Nodeで以下の指定で起動しておきます。

$ docker container run -it --rm -e USER=ispn-user -e PASS=ispn-password --name infinispan --hostname infinispan quay.io/infinispan/server-native:12.0.2.Final

IPアドレスを確認。

$ docker container inspect infinispan | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",

CLIで接続する

Dockerイメージで起動しているので、設定変更して再起動、というわけにはいきません。
起動時に設定ファイルをマウントしつつ環境変数を指定して設定する方法もあるのですが、今回はそこまでやりません。

なので、CLIを使うことになるわけですが…先ほどのfindの結果を見るとわかるように、Infinispan Serverのネイティブイメージには
CLIが含まれていません。

CLIは、Infinispan ServerのQuarkus Extensionのリポジトリに含まれています。ネイティブイメージです。

GitHub - infinispan/infinispan-quarkus: Infinispan Quarkus based extensions and Server

こちらをダウンロードして展開。CLIは、ホスト側で利用することにします。

$ curl -LOs https://github.com/infinispan/infinispan-quarkus/releases/download/12.0.2.Final/infinispan-cli-12.0.2.Final-linux-amd64.zip
$ unzip infinispan-cli-12.0.2.Final-linux-amd64.zip
$ cd infinispan-quarkus-cli-12.0.2.Final

CLIの中身は、これだけです。

$ ls -1
LICENSE.md
README.md
ispn-cli

ispn-cliが、CLIの本体ですね。ファイル形式を確認。

$ file ispn-cli
ispn-cli: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e2ac30e01729ed9a282efd755562fdbe00609f66, for GNU/Linux 3.2.0, not stripped

先ほど確認したIPアドレスと、ネイティブイメージの起動時にUSER、PASSで指定したユーザー名、パスワードを指定して接続してみます。

$ ./ispn-cli -c http://ispn-user:ispn-password@172.17.0.2:11222
[infinispan-50956@infinispan//containers/default]>

まだCacheはありません。

[infinispan-50956@infinispan//containers/default]> ls caches
___script_cache

作成しましょう。

テンプレートを指定して作成。

[infinispan-50956@infinispan//containers/default]> create cache --template=org.infinispan.DIST_SYNC distCache
[infinispan-50956@infinispan//containers/default]> ls caches
distCache
___script_cache

この時、Server側(コンテナ内)ではdata/caches.xmlというファイルに指定したCacheが増えています。

$ docker container exec -it infinispan cat data/caches.xml
<?xml version="1.0" ?>

<infinispan xmlns="urn:infinispan:config:12.0">
    <cache-container>
        <distributed-cache mode="SYNC" remote-timeout="17500" name="distCache" statistics="true">
            <locking concurrency-level="1000" acquire-timeout="15000" striping="false"/>
            <state-transfer timeout="60000"/>
        </distributed-cache>
    </cache-container></infinispan>

テンプレートを使うと簡単にCacheを作れますが、設定の自由度はありません。

テンプレートで定義されているCache定義はこちらですね。

https://github.com/infinispan/infinispan/blob/12.0.2.Final/server/runtime/src/main/resources/infinispan-defaults.xml

もう少し自由度が欲しい場合は、Cache定義を行ったXMLファイルを用意します。

cache-snippet.xml

<?xml version="1.0" ?>
<infinispan>
    <cache-container>
        <distributed-cache mode="SYNC" name="cache" />
    </cache-container>
</infinispan>

今回用意したのは、とても簡単な内容ですが…。

これを、CLIから指定します。XMLファイルは、CLIを実行する環境上に存在していればOKです。

[infinispan-50956@infinispan//containers/default]> create cache --file=cache-snippet.xml myCache

Cacheが増えました。

[infinispan-50956@infinispan//containers/default]> ls caches
___script_cache
myCache
distCache

Server側でもCacheが増えています。

$ docker container exec -it infinispan cat data/caches.xml
<?xml version="1.0" ?>

<infinispan xmlns="urn:infinispan:config:12.0">
    <cache-container>
        <distributed-cache mode="SYNC" name="myCache"/>
        <distributed-cache mode="SYNC" remote-timeout="17500" name="distCache" statistics="true">
            <locking concurrency-level="1000" acquire-timeout="15000" striping="false"/>
            <state-transfer timeout="60000"/>
        </distributed-cache>
    </cache-container></infinispan>

Hot Rod Clientから接続してみる

Cacheを作ってみたので、Hot Rod Clientから接続してみましょう。

前提とする環境は、こちらです。

$ java --version
openjdk 11.0.10 2021-01-19
OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)


$ jbang --version
0.69.2

簡単に、JBangで実行したいと思います。

こんなプログラムを用意。

App.java

//DEPS org.infinispan:infinispan-client-hotrod:12.0.2.Final

import java.net.SocketAddress;
import java.util.Map;
import java.util.Set;
import java.util.stream.IntStream;

import org.infinispan.client.hotrod.CacheTopologyInfo;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.Configuration;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;

public class App {
    public static void main(String... args) {
        Configuration configuration =
                new ConfigurationBuilder()
                        .uri("hotrod://ispn-user:ispn-password@172.17.0.2:11222")
                        .build();

        RemoteCacheManager manager = new RemoteCacheManager(configuration);

        RemoteCache<String, String> cache = manager.getCache("myCache");

        try {
            System.out.println("===== put data =====");

            IntStream
                    .rangeClosed(1, 10)
                    .forEach(i -> cache.put("key" + i, "value" + i));

            System.out.println("===== get data =====");

            IntStream
                    .rangeClosed(1, 10)
                    .forEach(i -> System.out.printf("  key%d: %s%n", i, cache.get("key" + i)));

            System.out.println("===== cache topology =====");
            CacheTopologyInfo topology = cache.getCacheTopologyInfo();
            for (Map.Entry<SocketAddress, Set<Integer>> segment : topology.getSegmentsPerServer().entrySet()) {
                System.out.printf("  %s : %s%n", segment.getKey(), segment.getValue());
            }
        } finally {
            cache.stop();
            manager.stop();
        }
    }
}

Maven依存関係は、こちらに書いてあります。infinispan-client-hotrodを使います。

//DEPS org.infinispan:infinispan-client-hotrod:12.0.2.Final

接続情報は、こちら。

        Configuration configuration =
                new ConfigurationBuilder()
                        .uri("hotrod://ispn-user:ispn-password@172.17.0.2:11222")
                        .build();

その他も、通常のHot Rod Clientと使い方は変わりません。

Cacheは、ファイルを元に作成したものを使いました。

        RemoteCache<String, String> cache = manager.getCache("myCache");

実行。

$ jbang App.java

動作しましたね。

3月 28, 2021 1:47:29 午前 org.infinispan.client.hotrod.RemoteCacheManager actualStart
INFO: ISPN004021: Infinispan version: Infinispan 'Lockdown' 12.0.2.Final
3月 28, 2021 1:47:29 午前 org.infinispan.client.hotrod.impl.protocol.Codec20 readNewTopologyAndHash
INFO: ISPN004006: Server sent new topology view (id=1, age=0) containing 1 addresses: [172.17.0.2:11222]
===== put data =====
===== get data =====
  key1: value1
  key2: value2
  key3: value3
  key4: value4
  key5: value5
  key6: value6
  key7: value7
  key8: value8
  key9: value9
  key10: value10
===== cache topology =====
  172.17.0.2:11222 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]

OKですね。

クラスタを構成してみる

1度、先ほどまで動かしていたDockerイメージを停止します。

そして、今度はInfinispan Serverのネイティブイメージでクラスタを構成してみましょう。

Infinispan Serverを3 Node起動します。

## 1.
$ docker container run -it --rm -e USER=ispn-user -e PASS=ispn-password --name infinispan1 --hostname infinispan1 quay.io/infinispan/server-native:12.0.2.Final


## 2.
$ docker container run -it --rm -e USER=ispn-user -e PASS=ispn-password --name infinispan2 --hostname infinispan2 quay.io/infinispan/server-native:12.0.2.Final


## 3.
$ docker container run -it --rm -e USER=ispn-user -e PASS=ispn-password --name infinispan3 --hostname infinispan3 quay.io/infinispan/server-native:12.0.2.Final

ログを見ると、クラスタが構成されました。

2021-03-27 16:50:32,208 INFO  [org.inf.CLUSTER] (jgroups-5,infinispan1-2518) ISPN000094: Received new cluster view for channel infinispan: [infinispan2-63714|2] (3) [infinispan2-63714, infinispan1-2518, infinispan3-44542]
2021-03-27 16:50:32,208 INFO  [org.inf.CLUSTER] (jgroups-5,infinispan1-2518) ISPN100000: Node infinispan3-44542 joined the cluster

IPアドレスを確認。172.17.0.2〜172.17.0.3です。

$ echo 'infinispan1
 infinispan2
 infinispan3' | xargs -I {} docker container inspect {} | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.3",
                    "IPAddress": "172.17.0.3",
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.4",
                    "IPAddress": "172.17.0.4",

CLIでも接続してみましょう。どのNodeでもよいのですが、ひとつ指定して接続してみます。

$ ./ispn-cli -c http://ispn-user:ispn-password@172.17.0.2:11222
[infinispan1-2518@infinispan//containers/default]> 

クラスタに、3 Nodeあることが確認できます。

[infinispan1-2518@infinispan//containers/default]> ls /cluster
infinispan2-63714
infinispan1-2518
infinispan3-44542

Cacheも作成しておきます。

[infinispan1-2518@infinispan//containers/default]> create cache --file=cache-snippet.xml myCache

ここで、先ほどのプログラムの接続情報を以下のように変更します。

        Configuration configuration =
                new ConfigurationBuilder()
                        .uri("hotrod://ispn-user:ispn-password@172.17.0.2:11222,172.17.0.3:11222,172.17.0.4:11222")
                        .build();

実行。

$ jbang App.java

3 Nodeを認識し、トポロジとしても認識しています。

INFO: ISPN004021: Infinispan version: Infinispan 'Lockdown' 12.0.2.Final
3月 28, 2021 1:56:29 午前 org.infinispan.client.hotrod.impl.protocol.Codec20 readNewTopologyAndHash
INFO: ISPN004006: Server sent new topology view (id=9, age=0) containing 3 addresses: [172.17.0.4:11222, 172.17.0.2:11222, 172.17.0.3:11222]
===== put data =====
===== get data =====
  key1: value1
  key2: value2
  key3: value3
  key4: value4
  key5: value5
  key6: value6
  key7: value7
  key8: value8
  key9: value9
  key10: value10
===== cache topology =====
  172.17.0.4:11222 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 178, 179, 180, 181, 189, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 253, 254, 255]
  172.17.0.2:11222 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]
  172.17.0.3:11222 : [52, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252]

OKですね。

ちなみに、Hot Rod URIで指定した各Nodeの接続先は、あくまで初期Nodeです。Hot Rod Clientは、クラスタに参加している
Nodeを知ることができるので、最初の定義に戻して

        Configuration configuration =
                new ConfigurationBuilder()
                        .uri("hotrod://ispn-user:ispn-password@172.17.0.2:11222")
                        .build();

実行しても

$ jbang App.java

ちゃんと3 Node認識します。

3月 28, 2021 1:58:31 午前 org.infinispan.client.hotrod.RemoteCacheManager actualStart
INFO: ISPN004021: Infinispan version: Infinispan 'Lockdown' 12.0.2.Final
3月 28, 2021 1:58:31 午前 org.infinispan.client.hotrod.impl.protocol.Codec20 readNewTopologyAndHash
INFO: ISPN004006: Server sent new topology view (id=9, age=0) containing 3 addresses: [172.17.0.4:11222, 172.17.0.2:11222, 172.17.0.3:11222]
3月 28, 2021 1:58:31 午前 org.infinispan.client.hotrod.impl.transport.netty.ChannelFactory updateTopologyInfo
INFO: ISPN004014: New server added(172.17.0.4:11222), adding to the pool.
3月 28, 2021 1:58:31 午前 org.infinispan.client.hotrod.impl.transport.netty.ChannelFactory updateTopologyInfo
INFO: ISPN004014: New server added(172.17.0.3:11222), adding to the pool.
===== put data =====
===== get data =====
  key1: value1
  key2: value2
  key3: value3
  key4: value4
  key5: value5
  key6: value6
  key7: value7
  key8: value8
  key9: value9
  key10: value10
===== cache topology =====
  172.17.0.4:11222 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 178, 179, 180, 181, 189, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 253, 254, 255]
  172.17.0.2:11222 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]
  172.17.0.3:11222 : [52, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252]

この部分ですね。

3月 28, 2021 1:58:31 午前 org.infinispan.client.hotrod.impl.transport.netty.ChannelFactory updateTopologyInfo
INFO: ISPN004014: New server added(172.17.0.4:11222), adding to the pool.
3月 28, 2021 1:58:31 午前 org.infinispan.client.hotrod.impl.transport.netty.ChannelFactory updateTopologyInfo
INFO: ISPN004014: New server added(172.17.0.3:11222), adding to the pool.

クラスタにも接続できたので、OKとしましょう。

まとめ

Infinispan Serverのネイティブイメージ(のDockerイメージ)を使ってみました。

使い方そのものは通常のInfinispan Serverとあまり変わりませんが、CLIが入っていないとか提供形態がDockerイメージのみという
差はあったりするのでそのあたりを押さえておけばなんとかなるかな、という感じですね。

ネイティブイメージなので、Server Taskや独自のモジュールをデプロイする、といったことはできない気がしますけどね。

Infinispan Serverの1形態として覚えておきましょう。

Infinispan Serverに、URIを指定してアクセスする(Hot Rod URI)

これは、なにをしたくて書いたもの?

Infinispan 11.0.0.Finalから、Infinispan Serverへの接続情報や設定をURIとして表現できるようになっています。

Blog: Hot Rod URI - Infinispan

Blog: Infinispan 11.0.0.Final - Infinispan

ちょっと興味があったので、そろそろ試しておこうかな、と。

Hot Rod URI

これまで、ConfigurationBuilderやhotrod.propertiesで指定した内容を、URIとして指定できる機能です。

フォーマットは、こちら。

hotrod[s]://[username:password]@host[:port][,host[:port]…​][?property=value[&property=value…​]]

認証が必要な場合は、ホスト名の前に[username:password]@が入ります。接続先のホストは、,区切りで複数指定することが
できます。

それ以外のプロパティは、QueryStringの形式で指定します。この時、hotrod.propertiesで指定していたinfinispan.client.hotrodという
プレフィックスは不要になります。

このURIは、ConfigurationBuilderで指定するか、hotrod.propertiesのinfinispan.client.hotrod.uriとして指定します。

見たところ、こちらのガイドにはHot Rod URIの記述はないようです。

Hot Rod Java Client Guide

現時点では、org.infinispan.client.hotrod.configurationパッケージのサマリーを見るのが良さそうですね。

org.infinispan.client.hotrod.configuration (Infinispan JavaDoc 12.0.2.Final API)

では、試してみましょう。

環境

今回の環境は、こちらです。

$ java --version
openjdk 11.0.10 2021-01-19
OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.10, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-70-generic", arch: "amd64", family: "unix"

Infinispan Serverは、12.0.2.Finalを使います。3つインスタンスを用意し、172.17.0.2〜172.17.0.4で動作しているものとします。

Infinispan Server側の準備

Infinispan Serverへのアクセスは、認証を必須にすることにします。

ユーザーを作成。グループはadminとします。

$ bin/cli.sh user create ispn-user -p ispn-password -g admin

続いて、server/conf/infinispan.xmlを編集します。

エンドポイントの設定は、こちら。

      <endpoints socket-binding="default" security-realm="default">
         <hotrod-connector>
            <authentication>
               <sasl qop="auth" server-name="infinispan"/>
            </authentication>
         </hotrod-connector>
         <rest-connector />
      </endpoints>

Cacheの設定は、adminグループに全権限を与える簡易なものにしておきます。Cache自体は、Distributed Cacheを使います。

   <cache-container name="default" statistics="true">
      <transport cluster="${infinispan.cluster.name:cluster}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>
   
      <security>
         <authorization>
            <identity-role-mapper/>
            <role name="ispn-user" permissions="ALL"/>
         </authorization>
      </security>

      <distributed-cache name="myCache">
         <security>
            <authorization roles="ispn-user"/>                            
         </security>
      </distributed-cache>
   </cache-container>

ここまでやったら、Infinispan Serverを再起動します。

クラスタには、3つNodeが存在している状態です。

$ bin/cli.sh -c http://ispn-user:ispn-password@localhost:11222
[ae83336a35bf-19567@cluster//containers/default]> ls /cluster
2084c2102a21-13797
cb222c8eeb1b-60045
ae83336a35bf-19567

テストコード準備

Maven依存関係およびプラグインの設定は、こちら。

    <dependencies>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-client-hotrod</artifactId>
            <version>12.0.2.Final</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.19.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

今回のテーマで必須なのは、infinispan-client-hotrodだけですね。

確認は、テストコードで行います。

src/test/java/org/littlewings/infinispan/client/connect/ConnectServerTest.java

package org.littlewings.infinispan.client.connect;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;

import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.Configuration;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class ConnectServerTest {

    // ここに、テストコードを書く

}

こちらをベースに進めていきます。

Hot Rod URIを使わずに、Infinispan Serverに接続する

比較のために、まずはHot Rod URIを使わずにInfinispan Serverに接続してみましょう。

    @Test
    public void usingMethodBasedConfiguration() {
        Configuration configuration =
                new ConfigurationBuilder()
                        .addServers("172.17.0.2:11222;172.17.0.3:11222;172.17.0.4:11222")
                        .connectionPool()
                        .maxActive(10)
                        .maxWait(10000)
                        .connectionTimeout(30000)
                        .socketTimeout(30000)
                        .security()
                        .authentication()
                        .saslMechanism("SCRAM-SHA-256")
                        .username("ispn-user")
                        .password("ispn-password".toCharArray())
                        .build();

        RemoteCacheManager manager = new RemoteCacheManager(configuration);

        Configuration c = manager.getConfiguration();

        assertThat(c.connectionPool().maxActive()).isEqualTo(10);
        assertThat(c.connectionPool().maxWait()).isEqualTo(10000);
        assertThat(c.connectionTimeout()).isEqualTo(30000);
        assertThat(c.socketTimeout()).isEqualTo(30000);
        assertThat(c.security().authentication().saslMechanism()).isEqualTo("SCRAM-SHA-256");

        try {
            RemoteCache<String, String> cache = manager.getCache("myCache");

            CompletableFuture<String> putAndGet =
                    cache
                            .putAsync("key1", "value1")
                            .thenCompose(v -> cache.getAsync("key1"));

            assertThat(putAndGet)
                    .succeedsWithin(Duration.ofSeconds(10))
                    .isEqualTo("value1");

            CompletableFuture<String> removeAndGet =
                    cache.removeAsync("key1")
                            .thenCompose(v -> cache.getAsync("key1"));

            assertThat(removeAndGet)
                    .succeedsWithin(Duration.ofSeconds(10))
                    .isNull();
        } finally {
            manager.stop();
        }
    }

設定は、こんな感じでConfigurationBuilderで指定します。

        Configuration configuration =
                new ConfigurationBuilder()
                        .addServers("172.17.0.2:11222;172.17.0.3:11222;172.17.0.4:11222")
                        .connectionPool()
                        .maxActive(10)
                        .maxWait(10000)
                        .connectionTimeout(30000)
                        .socketTimeout(30000)
                        .security()
                        .authentication()
                        .saslMechanism("SCRAM-SHA-256")
                        .username("ispn-user")
                        .password("ispn-password".toCharArray())
                        .build();

設定できていることは、RemoteCacheManagerから取得できるConfigurationを使って確認しましょう。

        RemoteCacheManager manager = new RemoteCacheManager(configuration);

        Configuration c = manager.getConfiguration();

        assertThat(c.connectionPool().maxActive()).isEqualTo(10);
        assertThat(c.connectionPool().maxWait()).isEqualTo(10000);
        assertThat(c.connectionTimeout()).isEqualTo(30000);
        assertThat(c.socketTimeout()).isEqualTo(30000);
        assertThat(c.security().authentication().saslMechanism()).isEqualTo("SCRAM-SHA-256");

ユーザー名およびパスワードは確認できませんが、これはInfinispan Serverに接続できたことをもってOKとします。

あとは、通常のInfinispanのHot Rod Clientの使い方と同じなので省略。

Hot Rod URIを使って、Infinispan Serverに接続する

続いて、Hot Rod URIを使ってInfinispan Serverに接続してみます。

    @Test
    public void usingUri() {
        Configuration configuration =
                new ConfigurationBuilder()
                        .uri("hotrod://ispn-user:ispn-password@172.17.0.2:11222,172.17.0.3:11222,172.17.0.4:11222?sasl_mechanism=SCRAM-SHA-256&connection_pool.max_active=10&connect_timeout=30000&socket_timeout=30000")
                        .connectionPool()
                        .maxWait(10000)
                        .build();

        RemoteCacheManager manager = new RemoteCacheManager(configuration);

        Configuration c = manager.getConfiguration();

        assertThat(c.connectionPool().maxActive()).isEqualTo(10);
        assertThat(c.connectionPool().maxWait()).isEqualTo(10000);
        assertThat(c.connectionTimeout()).isEqualTo(30000);
        assertThat(c.socketTimeout()).isEqualTo(30000);
        assertThat(c.security().authentication().saslMechanism()).isEqualTo("SCRAM-SHA-256");

        try {
            RemoteCache<String, String> cache = manager.getCache("myCache");

            CompletableFuture<String> putAndGet =
                    cache
                            .putAsync("key1", "value1")
                            .thenCompose(v -> cache.getAsync("key1"));

            assertThat(putAndGet)
                    .succeedsWithin(Duration.ofSeconds(10))
                    .isEqualTo("value1");

            CompletableFuture<String> removeAndGet =
                    cache.removeAsync("key1")
                            .thenCompose(v -> cache.getAsync("key1"));

            assertThat(removeAndGet)
                    .succeedsWithin(Duration.ofSeconds(10))
                    .isNull();
        } finally {
            manager.stop();
        }
    }

先ほどの例から変わったのは、ここだけですね。

                new ConfigurationBuilder()
                        .uri("hotrod://ispn-user:ispn-password@172.17.0.2:11222,172.17.0.3:11222,172.17.0.4:11222?sasl_mechanism=SCRAM-SHA-256&connection_pool.max_active=10&connect_timeout=30000&socket_timeout=30000")
                        .connectionPool()
                        .maxWait(10000)
                        .build();

ユーザー名およびパスワード、接続先のInfinispan Serverのリストはこんな感じで指定します。

ispn-user:ispn-password@172.17.0.2:11222,172.17.0.3:11222,172.17.0.4:11222

アドレス:ポートを複数並べる時は、ConfigurationBuilder#addServersの時は;が区切り文字でしたが、Hot Rod URIになると
,に変わるので注意です。

その他のプロパティは、QueryStringの形式でJavadocに記載のプロパティ名からinfinispan.client.hotrodを除いたものを
指定します。

org.infinispan.client.hotrod.configuration (Infinispan JavaDoc 12.0.2.Final API)

?sasl_mechanism=SCRAM-SHA-256&connection_pool.max_active=10&connect_timeout=30000&socket_timeout=30000

connection_pool.max_activeのように、.が入ってもOKです。

コネクションプールのmaxWaitのみメソッドで指定していますが、これはHot Rod URIによる指定も、メソッドによる指定も
両方を併用できることを確認しています。

RemoteCacheManagerから取得したConfigurationへのテスト内容は先ほどと同じなので、Hot Rod URIで指定した内容が
反映できていることが確認できたことになります。

        RemoteCacheManager manager = new RemoteCacheManager(configuration);

        Configuration c = manager.getConfiguration();

        assertThat(c.connectionPool().maxActive()).isEqualTo(10);
        assertThat(c.connectionPool().maxWait()).isEqualTo(10000);
        assertThat(c.connectionTimeout()).isEqualTo(30000);
        assertThat(c.socketTimeout()).isEqualTo(30000);
        assertThat(c.security().authentication().saslMechanism()).isEqualTo("SCRAM-SHA-256");

これで、Hot Rod URIを使って設定ができたことを確認できました。

なお、Hot Rod URIをパースしているのは、このあたりです。

https://github.com/infinispan/infinispan/blob/12.0.2.Final/client/hotrod-client/src/main/java/org/infinispan/client/hotrod/impl/HotRodURI.java

まとめ

前々から、接続情報をURIで指定できたらいいなーと思っていたので、これができるようになったのは良かったなと。

あと、認証設定などのいい復習になりました。

今回作成したソースコードは、こちらに置いています。

https://github.com/kazuhira-r/infinispan-getting-started/tree/master/remote-connect-uri