Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

複数プロセスからアクセスしたい場合はどうするの?

こんにちは。kimukimuです。

Android4.0は。。。実機待ちです。
なので、Infinispanを実際的に使用するためのやり方を調べてみました。

1.実際に使用される状況ってどんな感じでしょうか?

Infinispanが実際に使用される環境を考えると、
『キャッシュサーバ』と、『キャッシュサーバを利用するプロセス』は
下の図のように別プロセスになる可能性が高いと考えています。

理由は、キャッシュサーバには複数のプロセスがアクセスするため。
1プロセス内でだけキャッシュにアクセスを行う場合なら、
機能的にはInfinispanはファイルに保存できるMapにすぎません。

なので、複数プロセスからInfinispanにアクセスする方法を確認します。

2.Infinispanをバッチから起動する方法

Infinispanを解凍したときに出来るbin配下にある「startServer.bat」を使用します。
Linuxの場合は「startServer.sh」

中身は下記の通り。

■startServer.bat

setlocal enabledelayedexpansion

set LIB=
for %%f in (..\lib\*.jar) do set LIB=!LIB!;%%f
for %%f in (..\modules\memcached\lib\*.jar) do set LIB=!LIB!;%%f
for %%f in (..\modules\hotrod\lib\*.jar) do set LIB=!LIB!;%%f
for %%f in (..\modules\websocket\lib\*.jar) do set LIB=!LIB!;%%f
rem echo libs: %LIB%

set CP=%LIB%;..\infinispan-core.jar;..\modules\core\infinispan-server-memcached.jar;%CP%
set CP=..\modules\memcached\infinispan-server-memcached.jar;%CP%
set CP=..\modules\hotrod\infinispan-server-hotrod.jar;%CP%
set CP=..\modules\websocket\infinispan-server-websocket.jar;%CP%

java -classpath "%CP%" -Dbind.address=127.0.0.1 -Djava.net.preferIPv4Stack=true -Dlog4j.configuration=..\etc\log4j.xml org.infinispan.server.core.Main %*

簡単に言ってしまうと、
ライブラリを読み込んで引数をMainクラスに渡して起動、というだけですね。
引数には起動形態、設定ファイルパスなどを与えることができます。

尚、このMainクラス、ファイルパス的には下記の場所に存在します。
/server/core/src/main/scala/org/infinispan/server/core/Main.scala

ええ。JavaではなくScalaです。
そんなわけで、最初Main.javaを探して見つかりませんでした^^;
Written in Javaとありますが、実際のところは「JVM言語」で書かれているようです。
Scalaは最近注目度が高まっていますが、こういうところでもつかわれているようですね。

で、実際に「外部からアクセス可能、設定ファイルを指定して起動」の場合は
下記のコマンドで実行します。

startServer.bat -r hotrod -c ..\config\infinispan-local.xml

ちなみに、今回用いた設定ファイルは下記の通り。
2つキャッシュを設定して、各々がファイルに出力する方式を取っています。

<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="urn:infinispan:config:5.0 http://www.infinispan.org/schemas/infinispan-config-5.0.xsd"
  xmlns="urn:infinispan:config:5.0">
  <global>
  </global>
  <default>
    <!-- Configure a synchronous replication cache -->
    <clustering mode="local">
      <sync />
    </clustering>
  </default>

  <!-- localUserCacheという名称のキャッシュ設定 -->
  <namedCache name="localUserCache">
    <clustering mode="local">
      <async/>
    </clustering>
    <loaders passivation="false" shared="false" preload="true">
      <loader class="org.infinispan.loaders.file.FileCacheStore"
        fetchPersistentState="true" ignoreModifications="false"
        purgeOnStartup="false">
        <properties>
          <property name="location" value="store" />
        </properties>
      </loader>
    </loaders>
  </namedCache>

  <!-- localServiceCacheという名称のキャッシュ設定 -->
  <namedCache name="localServiceCache">
    <clustering mode="local">
      <async/>
    </clustering>
    <loaders passivation="false" shared="false" preload="true">
      <loader class="org.infinispan.loaders.file.FileCacheStore"
        fetchPersistentState="true" ignoreModifications="false"
        purgeOnStartup="false">
        <properties>
          <property name="location" value="store" />
        </properties>
      </loader>
    </loaders>
  </namedCache>
</infinispan>

3.起動したInfinispanに対して外部からアクセスする方法

hotrodでInfinispanを起動したため、
次はhotrod-clientを用いて起動したInfinispanに対してアクセスを行います。
その際、modules/hotrod-client配下にある下記の2つのjarをライブラリに追加する必要があります。

infinispan-client-hotrod.jar
commons-pool-1.5.4.jar

値を設定するプログラムと、値を取得するプログラムに分けて記述します。
■InfinispanRemoteGetMain.java

package jp.gr.t3.infinispan.remote;

import org.infinispan.Cache;
import org.infinispan.client.hotrod.RemoteCacheManager;

public class InfinispanRemoteGetMain {
  public static void main(String[] args) throws Exception {
    new InfinispanRemoteGetMain().run();
  }

  public void run() {
    RemoteCacheManager cachemanager = new RemoteCacheManager("localhost");
    Cache<String, InfinispanUser> userCache = cachemanager
        .getCache("localUserCache");
    Cache<String, InfinispanService> serviceCache = cachemanager
        .getCache("localServiceCache");

    int counter = 0;

    while (true) {
      InfinispanUser getUser = userCache.get("UserKey");
      System.out.println("Get User : " + getUser);

      InfinispanService getService = serviceCache.get("ServiceKey");
      System.out.println("Get Service : " + getService);

      try {
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      counter++;
    }
  }
}

■InfinispanRemotePutMain.java

package jp.gr.t3.infinispan.remote;

import org.infinispan.Cache;
import org.infinispan.client.hotrod.RemoteCacheManager;

public class InfinispanRemotePutMain {
  public static void main(String[] args) throws Exception {
    new InfinispanRemotePutMain().run();
  }

  public void run() {
    RemoteCacheManager cachemanager = new RemoteCacheManager("localhost");
    Cache<String, InfinispanUser> userCache = cachemanager
        .getCache("localUserCache");
    Cache<String, InfinispanService> serviceCache = cachemanager
        .getCache("localServiceCache");

    int counter = 0;

    while (true) {
      InfinispanUser putUser = new InfinispanUser();
      putUser.userId = "UserId" + Integer.toString(counter);
      putUser.userName = "UserName" + Integer.toString(counter + 1);
      userCache.put("UserKey", putUser);

      InfinispanService putService = new InfinispanService();
      putService.serviceId = "ServiceId" + Integer.toString(counter * 2);
      putService.serviceName = "ServiceName"
          + Integer.toString(counter * 3);
      serviceCache.put("ServiceKey", putService);

      System.out.println("Put Counter : " + counter);

      try {
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      counter++;
    }
  }
}

すると、結果は下記のようになります。
■Put側

Put Counter : 0
Put Counter : 1
Put Counter : 2
Put Counter : 3
Put Counter : 4
Put Counter : 5
Put Counter : 6
Put Counter : 7
Put Counter : 8
Put Counter : 9
Put Counter : 10
Put Counter : 11

■Get側

Get User : UserId=UserId1,UserName=UserName2
Get Service : ServiceId=ServiceId2,ServiceName=ServiceName3
Get User : UserId=UserId2,UserName=UserName3
Get Service : ServiceId=ServiceId4,ServiceName=ServiceName6
Get User : UserId=UserId3,UserName=UserName4
Get Service : ServiceId=ServiceId6,ServiceName=ServiceName9
Get User : UserId=UserId4,UserName=UserName5
Get Service : ServiceId=ServiceId8,ServiceName=ServiceName12
Get User : UserId=UserId5,UserName=UserName6
Get Service : ServiceId=ServiceId10,ServiceName=ServiceName15
Get User : UserId=UserId6,UserName=UserName7
Get Service : ServiceId=ServiceId12,ServiceName=ServiceName18
Get User : UserId=UserId7,UserName=UserName8
Get Service : ServiceId=ServiceId14,ServiceName=ServiceName21
Get User : UserId=UserId8,UserName=UserName9
Get Service : ServiceId=ServiceId16,ServiceName=ServiceName24

これで、複数プロセスからサーバとして立ち上げたInfinispanにアクセスすることが確認できました。
また、その際に複数のキャッシュに別途アクセスできることも確認できました。

4.何が嬉しいの?

一番良くあるモデルだと思われる、下記のモデルが実現できるようになります。

Infinispanをサーバとして起動
多数のクライアントプロセスから必要な分だけアクセス

複数プロセス間でのデータ共有がマップに対するGet/Putだけで
出来るようになるわけです。

これなら、いろんな場面で実際に使えそうですね。