Hazelcast 3.3のリリースから、Replicated Mapというデータ構造が追加されました。
What's New in Hazelcast 3.3 - New Features
http://docs.hazelcast.org/docs/3.3/manual/html-single/hazelcast-documentation.html#new-features
Replicated Map - BETA
http://docs.hazelcast.org/docs/3.3/manual/html-single/hazelcast-documentation.html#replicated-map-beta
まだベータ版的な扱い?
Hazelcastといえば、MapやList、Queueといった多様なデータ構造を提供していますが、すべて分散(Distributed)なもので、各Nodeに分散してデータを持ちます。よって、バックアップ数以上のNodeが同時にダウンすると、データが失われます。
で、今回のReplicated Mapは、どのNodeも同じデータを持つようなMapになります。よって、1番メモリの少ないNodeに保持可能なデータ量が合わせられる形になるでしょうね。
このReplicated Mapですが、他のインメモリ・データグリッドのレプリケーションを行うものと比べて、ちょっと性格が異なるようです。
HazelcastのReplicated Mapの特徴は、
- 弱い一貫性
- 全Nodeが同じデータを持ち、メモリ消費量は大きいが読み取りは高速
- java.util.Mapインターフェースを実装しているが、ConcurrentMapではない(アトミックな読み書きはできない)
- 書き込み順は保証していない。2つのNodeから同時に書き込まれた場合は、Vector Clockアルゴリズムによりひとつの値を使用するように決定する
- Map#clearは非同期に実行される
結果、HTMLや商品データのような、あまり変わらないデータが格納されることを想定しているそうな。
使ってみる
まあ、概要はこれくらいにして、まずは使ってみましょう。
Maven依存関係は、このように定義。
<dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast</artifactId> <version>3.3.1</version> </dependency>
ごく単純なサンプルを書いてみます。
src/main/java/org/littlewings/hazelcast/replicatedmap/ReplicatedMapExample.java
package org.littlewings.hazelcast.replicatedmap; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; import com.hazelcast.config.Config; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; public class ReplicatedMapExample { public static void main(String[] args) { Config config = new Config(); HazelcastInstance hazelcast = Hazelcast.newHazelcastInstance(config); Map<String, String> map = hazelcast.getReplicatedMap("default"); // Map<String, String> map = hazelcast.getMap("default"); int entrySize = 20; IntStream .rangeClosed(1, entrySize) .forEach(i -> map.put("key" + i, "value" + i)); System.out.printf("[%s] Hazelcast Node, startup, putted [%d]entries.%n", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), entrySize); System.console().readLine("Enter, shutdown..."); IntStream .rangeClosed(1, entrySize) .forEach(i -> { String key = "key" + i; String value = map.get(key); System.out.printf("Key = [%s], Value = [%s]%n", key, value); }); hazelcast.getLifecycleService(); Hazelcast.shutdownAll(); } }
HazelcastInstance#getReplicatedMapで、Replicated Mapを取得することができます。
Map<String, String> map = hazelcast.getReplicatedMap("default");
今回はMapインターフェースで受けていますが、com.hazelcast.core.ReplicatedMapインターフェースで受けてもOKです。
このプログラム、20個エントリを放り込んで、Enterを打たれるまで待機します。
int entrySize = 20; IntStream .rangeClosed(1, entrySize) .forEach(i -> map.put("key" + i, "value" + i)); System.out.printf("[%s] Hazelcast Node, startup, putted [%d]entries.%n", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), entrySize); System.console().readLine("Enter, shutdown...");
その後、データをダンプしてシャットダウンします。
IntStream .rangeClosed(1, entrySize) .forEach(i -> { String key = "key" + i; String value = map.get(key); System.out.printf("Key = [%s], Value = [%s]%n", key, value); }); hazelcast.getLifecycleService(); Hazelcast.shutdownAll();
これを使って、単一のNodeがすべてのデータを持っていることを確認してみましょう。
このプログラムを4つ起動して、Hazelcast 4Nodeのクラスタを構成してみます。
# Node1 $ mvn compile exec:java -Dexec.mainClass=org.littlewings.hazelcast.replicatedmap.ReplicatedMapExample # Node2 $ mvn compile exec:java -Dexec.mainClass=org.littlewings.hazelcast.replicatedmap.ReplicatedMapExample # Node3 $ mvn compile exec:java -Dexec.mainClass=org.littlewings.hazelcast.replicatedmap.ReplicatedMapExample # Node4 $ mvn compile exec:java -Dexec.mainClass=org.littlewings.hazelcast.replicatedmap.ReplicatedMapExample
起動すると、各Nodeがこんなところで止まります。
[2014-10-12 16:21:35] Hazelcast Node, startup, putted [20]entries. Enter, shutdown...
全Nodeが起動し終わると、最初に起動したNodeのコンソールにはこんな表示が出ていると思います。
Members [4] { Member [192.168.129.129]:5701 this Member [192.168.129.129]:5702 Member [192.168.129.129]:5703 Member [192.168.129.129]:5704 }
ここから、各Nodeを落としてひとつだけ残します。
作ったプログラムの関係上、Enterを押していってもシャットダウンしていくのですが、それだとクラスタ再構成の猶予ができるので、ここは一気にkillします。
$ kill [Node2のPID] [Node3のPID] [Node4のPID]
Node1は、こういう状態になります。
Members [1] { Member [192.168.129.129]:5701 this }
あとは、Enterを押してシャットダウン。
Key = [key1], Value = [value1] Key = [key2], Value = [value2] Key = [key3], Value = [value3] Key = [key4], Value = [value4] Key = [key5], Value = [value5] Key = [key6], Value = [value6] Key = [key7], Value = [value7] Key = [key8], Value = [value8] Key = [key9], Value = [value9] Key = [key10], Value = [value10] Key = [key11], Value = [value11] Key = [key12], Value = [value12] Key = [key13], Value = [value13] Key = [key14], Value = [value14] Key = [key15], Value = [value15] Key = [key16], Value = [value16] Key = [key17], Value = [value17] Key = [key18], Value = [value18] Key = [key19], Value = [value19] Key = [key20], Value = [value20]
ダンプした値に、すべてのエントリが格納されていることが確認できます。
次に、HazelcastInstance#getReplicatedMapをHazelcastInstance#getMapに書き換え、Replicated MapからDistributed Mapに替えて同じことをしてみます。
// Map<String, String> map = hazelcast.getReplicatedMap("default"); Map<String, String> map = hazelcast.getMap("default");
各Nodeを起動して、同じようにkillします。
$ kill [Node2のPID] [Node3のPID] [Node4のPID]
残ったNodeで、Enterを押します。
Key = [key1], Value = [null] Key = [key2], Value = [null] Key = [key3], Value = [value3] Key = [key4], Value = [null] Key = [key5], Value = [null] Key = [key6], Value = [value6] Key = [key7], Value = [value7] Key = [key8], Value = [value8] Key = [key9], Value = [value9] Key = [key10], Value = [null] Key = [key11], Value = [value11] Key = [key12], Value = [value12] Key = [key13], Value = [null] Key = [key14], Value = [null] Key = [key15], Value = [null] Key = [key16], Value = [null] Key = [key17], Value = [value17] Key = [key18], Value = [null] Key = [key19], Value = [value19] Key = [key20], Value = [null]
こちらでは、エントリが欠けていますね。
Replicated Mapを使うと、各Nodeが全データを持つことが確認できたと思います。次は、設定などについて見ていこうと思います。
続きは、こちら。
HazelcastのReplicated Mapを試してみる - その2
http://d.hatena.ne.jp/Kazuhira/20141012/1413105032
作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/hazelcast-examples/tree/master/hazelcast-replicated-map