Apache Geodeで、はじめてのクラスタリングにチャレンジ。
ドキュメントに沿っていくと、Client/Server構成が基本路線かとも思いましたが、いろいろあってPeer-to-Peerでやることにしました。
今回のとっかかりとして、参考にしたのはこのあたりのドキュメントです。
http://geode.docs.pivotal.io/docs/topologies_and_comm/p2p_configuration/setting_up_a_p2p_system.html
http://geode.docs.pivotal.io/docs/configuring/running/running_the_locator.html
http://geode.docs.pivotal.io/docs/reference/topics/gemfire_properties.html
とりあえず、各NodeにLocatorがあればなんとかクラスタ構成できそうだったので、その線で進めてみました。
今回のサンプルでとる構成は、簡単なCLIツールと、単に浮いているだけのサーバーの2種類とします。クラスタ構成後、CLIツールでちょっと動きを確認してみる、そんな感じでやります。
準備
まずはMaven依存関係から。使ったApache Geodeのバージョンは、1.0.0-incubating.M1です。
<dependency> <groupId>org.apache.geode</groupId> <artifactId>gemfire-core</artifactId> <version>1.0.0-incubating.M1</version> </dependency>
Geodeの設定ファイルは、次の2つを用意。
Locatorとして、ポート10335、10336をListenしているものを初期メンバーとして想定。
src/main/resources/gemfire.properties
locators=localhost[10335],localhost[10336]
Nodeは今回3つ用意しますが、もうひとつは初期メンバーに対して動的に追加する感じで。
Cacheの設定は、PARTITIONとREPLICATEでひとつずつ。
src/main/resources/cache.xml
<?xml version="1.0" encoding="UTF-8"?> <cache xmlns="http://schema.pivotal.io/gemfire/cache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schema.pivotal.io/gemfire/cache http://schema.pivotal.io/gemfire/cache/cache-8.1.xsd" version="8.1"> <region name="partitionRegion" refid="PARTITION"> <region-attributes> <partition-attributes redundant-copies="1"/> </region-attributes> </region> <region name="replicateRegion" refid="REPLICATE"/> </cache>
PARTITIONの方は、バックアップをひとつ取るようにしました。
※もしくは、後述のPARTITION_REDUNDANTを使うとよいかも
gemfire.properties、cache.xmlはGeodeのデフォルトの設定ファイルの名前なので、特に指定せずとも読み込んでくれます。
この設定で、確認用のプログラムを書いてみます。
動作確認用のプログラム
単純に、Nodeとして浮いていてもらうようのプログラム。
src/main/java/org/littlewings/geode/Server.java
package org.littlewings.geode; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheFactory; public class Server { public static void main(String... args) { Cache cache = new CacheFactory() .create(); System.console().readLine("> Enter, stop server."); cache.close(); } }
Geodeを起動して、Enterを押したら終了する簡単なものです。
続いて、CLIツール。
src/main/java/org/littlewings/geode/Cli.java
package org.littlewings.geode; import java.io.Console; import java.util.stream.IntStream; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheFactory; import com.gemstone.gemfire.cache.Region; import com.gemstone.gemfire.distributed.DistributedSystem; public class Cli { public static void main(String... args) { Cache cache = new CacheFactory() .create(); try { Console console = System.console(); boolean cliContinue = true; while (cliContinue) { String line = console.readLine("> "); if (line == null || line.trim().isEmpty()) { continue; } try { Region<String, String> region; String[] tokens = line.split("\\s+"); switch (tokens[0]) { case "put": region = cache.getRegion(tokens[1]); region.put(tokens[2], tokens[3]); System.out.printf("Region[%s] putted, key = %s, value = %s.%n", region.getName(), tokens[2], tokens[3]); break; case "get": region = cache.getRegion(tokens[1]); System.out.printf("Region[%s] get, key = %s, value = %s.%n", region.getName(), tokens[2], region.get(tokens[2])); break; case "members": if (tokens.length == 1) { DistributedSystem ds = cache.getDistributedSystem(); System.out.printf("Self = %s.%n", ds.getDistributedMember()); System.out.printf("Other Members = %s.%n", ds.getAllOtherMembers()); System.out.printf("Other Members, alias = %s.%n", cache.getMembers()); } else { region = cache.getRegion(tokens[1]); System.out.printf("Region[%s] Other Members = %s.%n", region.getName(), cache.getMembers(region)); } break; case "range-put": region = cache.getRegion(tokens[1]); int start = Integer.parseInt(tokens[2]); int end = Integer.parseInt(tokens[3]); IntStream.rangeClosed(start , end).forEach(i -> { String key = "key" + i; String value = "value" + i; region.put(key, value); System.out.printf("Region[%s] putted, key = %s, value = %s.%n", region.getName(), key, value); }); break; case "get-all": region = cache.getRegion(tokens[1]); region .entrySet() .stream() .forEach(entry -> System.out.printf("Region[%s] key = %s, value = %s%n", region.getName(), entry.getKey(), entry.getValue())); break; case "exit": cliContinue = false; break; default: System.out.printf("Unknown Command[%s].%n", line); break; } } catch (Exception e) { System.out.printf("Unknown Command, or Parse Error, [%s].%n", line); } } } finally { cache.close(); } } }
put、get、範囲を指定して一括put(単純なルールでのKey/Value)、エントリすべてのget、クラスタに参加しているメンバーの出力、といったあたりのコマンドを用意。
確認
それでは、作ったプログラムで動作確認してみます。
今回は、Serverを2 Node、加えてCLIの計3 Nodeとします。
Serverを2 Node起動。
## Node 1 $ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.Server -Dgemfire.start-locator=localhost[10335] ## Node 2 $ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.Server -Dgemfire.start-locator=localhost[10336]
システムプロパティに「gemfire.」接頭辞を付与することで、gemfire.propertiesで設定できる内容を外部から指定できるようです。
また、ちゃんとNodeが起動しきってから、次のNodeを起動するのがよいみたいです…。
今回は、Locatorを開始するのと、ポートを被らせないようにしています。また、Locatorのデフォルトポートは10334ですが、クラスタが組めていることを確認するために、ずらしておきました。
続いて、CLIツールを起動。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.Cli -Dgemfire.start-locator=localhost[10337]
こちらのLocatorは、10337ポートを利用。
起動時に、3 Nodeいるっぽいログが出力されます。
[info 2016/02/24 20:53:47.104 JST <unicast receiver,xxxxx-9503> tid=0x1b] Peer locator received new membership view: View[192.168.254.129(34250)<ec><v0>:1024|2] members: [192.168.254.129(34250)<ec><v0>:1024{lead}, 192.168.254.129(34318)<ec><v1>:1025, 192.168.254.129(34407)<ec><v2>:1026]
参加メンバー確認。
> members Self = 192.168.254.129(34407)<ec><v2>:1026. Other Members = [192.168.254.129(34250)<ec><v0>:1024, 192.168.254.129(34318)<ec><v1>:1025]. Other Members, alias = [192.168.254.129(34250)<ec><v0>:1024, 192.168.254.129(34318)<ec><v1>:1025].
自分以外に、2 Nodeいます。
Regionを指定してput。
> put partitionRegion key1 value1 Region[partitionRegion] putted, key = key1, value = value1.
get。
> get partitionRegion key1 Region[partitionRegion] get, key = key1, value = value1. > get partitionRegion key2 [info 2016/02/24 20:55:12.005 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_86 [info 2016/02/24 20:55:12.008 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_86 completed Region[partitionRegion] get, key = key2, value = null.
2つめは、エントリを登録していないのでnull。
一括登録。
> range-put partitionRegion 1 10 Region[partitionRegion] putted, key = key1, value = value1. Region[partitionRegion] putted, key = key2, value = value2. Region[partitionRegion] putted, key = key3, value = value3. [info 2016/02/24 20:55:24.957 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_88 [info 2016/02/24 20:55:24.959 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_88 completed Region[partitionRegion] putted, key = key4, value = value4. [info 2016/02/24 20:55:25.008 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_89 [info 2016/02/24 20:55:25.015 JST <org.littlewings.geode.Cli.main()> tid=0xb] Region _B__partitionRegion_89 requesting initial image from 192.168.254.129(34250)<ec><v0>:1024 [info 2016/02/24 20:55:25.017 JST <org.littlewings.geode.Cli.main()> tid=0xb] _B__partitionRegion_89 is done getting image from 192.168.254.129(34250)<ec><v0>:1024. isDeltaGII is false [info 2016/02/24 20:55:25.018 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_89 completed Region[partitionRegion] putted, key = key5, value = value5. [info 2016/02/24 20:55:25.049 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_90 [info 2016/02/24 20:55:25.056 JST <org.littlewings.geode.Cli.main()> tid=0xb] Region _B__partitionRegion_90 requesting initial image from 192.168.254.129(34318)<ec><v1>:1025 [info 2016/02/24 20:55:25.058 JST <org.littlewings.geode.Cli.main()> tid=0xb] _B__partitionRegion_90 is done getting image from 192.168.254.129(34318)<ec><v1>:1025. isDeltaGII is false [info 2016/02/24 20:55:25.058 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_90 completed Region[partitionRegion] putted, key = key6, value = value6. [info 2016/02/24 20:55:25.078 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_91 [info 2016/02/24 20:55:25.080 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_91 completed Region[partitionRegion] putted, key = key7, value = value7. Region[partitionRegion] putted, key = key8, value = value8. [info 2016/02/24 20:55:25.156 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_93 [info 2016/02/24 20:55:25.161 JST <org.littlewings.geode.Cli.main()> tid=0xb] Region _B__partitionRegion_93 requesting initial image from 192.168.254.129(34318)<ec><v1>:1025 [info 2016/02/24 20:55:25.164 JST <org.littlewings.geode.Cli.main()> tid=0xb] _B__partitionRegion_93 is done getting image from 192.168.254.129(34318)<ec><v1>:1025. isDeltaGII is false [info 2016/02/24 20:55:25.165 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_93 completed Region[partitionRegion] putted, key = key9, value = value9. [info 2016/02/24 20:55:25.178 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initializing region _B__partitionRegion_84 [info 2016/02/24 20:55:25.183 JST <org.littlewings.geode.Cli.main()> tid=0xb] Initialization of region _B__partitionRegion_84 completed Region[partitionRegion] putted, key = key10, value = value10.
なんか、ちらちらとログが見えますね…。
全部取得。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 Region[partitionRegion] key = key1, value = value1 Region[partitionRegion] key = key2, value = value2 Region[partitionRegion] key = key3, value = value3 Region[partitionRegion] key = key4, value = value4 Region[partitionRegion] key = key5, value = value5 Region[partitionRegion] key = key6, value = value6 Region[partitionRegion] key = key7, value = value7 Region[partitionRegion] key = key8, value = value8 Region[partitionRegion] key = key9, value = value9
こちらもログが出るんですけど、端折りました。
1 Node落としてみる
ここで、Server Nodeをひとつ落としてみます。
[info 2016/02/24 20:56:01.008 JST <org.littlewings.geode.Server.main()> tid=0xb] GemFireCache[id = 1690853966; isClosing = true; isShutDownAll = false; created = Wed Feb 24 20:53:13 JST 2016; server = false; copyOnRead = false; lockLease = 120; lockTimeout = 60]: Now closing. [info 2016/02/24 20:56:01.138 JST <org.littlewings.geode.Server.main()> tid=0xb] Shutting down DistributionManager 192.168.254.129(34250)<ec><v0>:1024. [info 2016/02/24 20:56:01.241 JST <org.littlewings.geode.Server.main()> tid=0xb] Now closing distribution for 192.168.254.129(34250)<ec><v0>:1024 [info 2016/02/24 20:56:01.242 JST <org.littlewings.geode.Server.main()> tid=0xb] Stopping membership services [info 2016/02/24 20:56:02.244 JST <org.littlewings.geode.Server.main()> tid=0xb] GMSHealthMonitor server socket is closed in stopServices(). [info 2016/02/24 20:56:02.245 JST <Geode Failure Detection Server thread 0> tid=0x1f] GMSHealthMonitor server thread exiting [info 2016/02/24 20:56:02.245 JST <org.littlewings.geode.Server.main()> tid=0xb] GMSHealthMonitor serverSocketExecutor is terminated [info 2016/02/24 20:56:02.255 JST <org.littlewings.geode.Server.main()> tid=0xb] DistributionManager stopped in 1,117ms. [info 2016/02/24 20:56:02.255 JST <org.littlewings.geode.Server.main()> tid=0xb] Marking DistributionManager 192.168.254.129(34250)<ec><v0>:1024 as closed. [info 2016/02/24 20:56:02.256 JST <org.littlewings.geode.Server.main()> tid=0xb] Stopping Distribution Locator on localhost/127.0.0.1[10335] [info 2016/02/24 20:56:02.257 JST <Distribution Locator on localhost/127.0.0.1[10335]> tid=0x10] locator shutting down [info 2016/02/24 20:56:02.259 JST <org.littlewings.geode.Server.main()> tid=0xb] Distribution Locator on localhost/127.0.0.1[10335] is stopped
参加メンバー確認。
> members Self = 192.168.254.129(34407)<ec><v2>:1026. Other Members = [192.168.254.129(34318)<ec><v1>:1025]. Other Members, alias = [192.168.254.129(34318)<ec><v1>:1025].
1 Node減りましたね。
データ確認。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 Region[partitionRegion] key = key1, value = value1 Region[partitionRegion] key = key2, value = value2 Region[partitionRegion] key = key3, value = value3 Region[partitionRegion] key = key4, value = value4 Region[partitionRegion] key = key5, value = value5 Region[partitionRegion] key = key6, value = value6 Region[partitionRegion] key = key7, value = value7 Region[partitionRegion] key = key8, value = value8 Region[partitionRegion] key = key9, value = value9
ちゃんと、全部残っていますね。
PARTITIONで設定していたredundant-copiesはデフォルト値が0なので、そのままだとNodeダウン時にデータが欠損します。
redundant-copiesを1以上にするか、もしくはRegion ShortcutsでPARTITIONではなくPARTITION_REDUNDANTを指定するとよいでしょう。
http://geode.docs.pivotal.io/docs/reference/topics/region_shortcuts_table.html
ここで、再度Node起動。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.Server -Dgemfire.start-locator=localhost[10335]
Nodeが3つに戻りました。
> members Self = 192.168.254.129(34407)<ec><v2>:1026. Other Members = [192.168.254.129(34585)<ec><v13>:1024, 192.168.254.129(34318)<ec><v1>:1025]. Other Members, alias = [192.168.254.129(34585)<ec><v13>:1024, 192.168.254.129(34318)<ec><v1>:1025].
PARTITIONとREPLICATEの違いを見る
今度は、PARTITIONとREPLICATEの違いを見てみましょう。
それぞれのRegionに、データを登録します。
PARTITION。
> range-put partitionRegion 1 10 Region[partitionRegion] putted, key = key1, value = value1. Region[partitionRegion] putted, key = key2, value = value2. Region[partitionRegion] putted, key = key3, value = value3. Region[partitionRegion] putted, key = key4, value = value4. Region[partitionRegion] putted, key = key5, value = value5. Region[partitionRegion] putted, key = key6, value = value6. Region[partitionRegion] putted, key = key7, value = value7. Region[partitionRegion] putted, key = key8, value = value8. Region[partitionRegion] putted, key = key9, value = value9. Region[partitionRegion] putted, key = key10, value = value10.
REPLICATE。
> range-put replicateRegion 1 10 Region[replicateRegion] putted, key = key1, value = value1. Region[replicateRegion] putted, key = key2, value = value2. Region[replicateRegion] putted, key = key3, value = value3. Region[replicateRegion] putted, key = key4, value = value4. Region[replicateRegion] putted, key = key5, value = value5. Region[replicateRegion] putted, key = key6, value = value6. Region[replicateRegion] putted, key = key7, value = value7. Region[replicateRegion] putted, key = key8, value = value8. Region[replicateRegion] putted, key = key9, value = value9. Region[replicateRegion] putted, key = key10, value = value10.
ここで、Server Nodeを2つ同時にkillします。
$ kill 34585 34318
CLI側に残るのは、自分のみです。
> members Self = 192.168.254.129(34407)<ec><v2>:1026. Other Members = []. Other Members, alias = [].
データの状態を見てみましょう。
PARTITION。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 Region[partitionRegion] key = key2, value = value2 Region[partitionRegion] key = key4, value = value4 Region[partitionRegion] key = key5, value = value5 Region[partitionRegion] key = key6, value = value6 Region[partitionRegion] key = key7, value = value7 Region[partitionRegion] key = key9, value = value9
こちらは、1 Nodeにバックアップを用意しているだけなので、2 Node同時に落とすと微妙に欠損します。
REPLICATE。
> get-all replicateRegion Region[replicateRegion] key = key9, value = value9 Region[replicateRegion] key = key10, value = value10 Region[replicateRegion] key = key8, value = value8 Region[replicateRegion] key = key6, value = value6 Region[replicateRegion] key = key7, value = value7 Region[replicateRegion] key = key2, value = value2 Region[replicateRegion] key = key4, value = value4 Region[replicateRegion] key = key1, value = value1 Region[replicateRegion] key = key5, value = value5 Region[replicateRegion] key = key3, value = value3
こちらは、全データを全Nodeにコピーするタイプなので、データが残ったままですね。
まとめ
はじめてApache Geodeでクラスタを組んでみましたが、Nodeがクラスタに参加するところで若干てこずった感じです。
Node Discoveryについて、もうちょっと見た方がいいかな?
あと、Locatorはプログラム内で開始する方法も覚えたいなと思います。
http://geode.docs.pivotal.io/docs/configuring/running/running_the_locator.html
とりあえず、クラスタさえ構成できれば少しはなんとかなりそうです…。