CLOVER🍀

That was when it all began.

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

これまで、ConfigurationBuilderhotrod.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.propertiesinfinispan.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