Hazelcast提供了很多通用數(shù)據(jù)結(jié)構(gòu)的分布式實(shí)現(xiàn)。針對(duì)每一種不同的客戶端語言,Hazelcast都盡量模擬該語言原生接口,對(duì)于Java客戶端,Hazelcast提供的IMap 和java.util.Map 有近似的語義。為了更加直觀的描述,針對(duì)Hazelcast中的數(shù)據(jù)結(jié)構(gòu),我們將提供Java中等效或類似接口。 所有這些結(jié)構(gòu)都可以在Java,.NET,C ++,Node.js,Python,Go和Scala客戶端中使用。
-
標(biāo)準(zhǔn)集合
- Map:
java.util.Map的分布式實(shí)現(xiàn)。 - Queue:
java.util.concurrent.BlockingQueue的分布式實(shí)現(xiàn)。 - Ringbuffer:Java中沒有對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),Ringbuffer通常用于可靠的事件系統(tǒng)。
- Set:
java.util.Set的分布式并發(fā)實(shí)現(xiàn)。 - List:
java.util.List的分布式并發(fā)實(shí)現(xiàn),支持存儲(chǔ)重復(fù)元素,這是和Set的唯一區(qū)別。 - Multimap:
com.google.common.collect.Multimap的分布式實(shí)現(xiàn),支持存儲(chǔ)重復(fù)鍵。 - Replicated Map:不支持分區(qū)的
Map數(shù)據(jù)結(jié)構(gòu),集群所有成員都有全量數(shù)據(jù)。 - Cardinality Estimator :實(shí)現(xiàn)了lajolet’s HyperLogLog 算法的數(shù)據(jù)結(jié)構(gòu)。
- Map:
-
主題
主題是用于多個(gè)訂閱者的分布式消息分發(fā)機(jī)制,和kakfa、pulsar中的主題一樣,同樣也是pub/sub消息模型的關(guān)鍵。Hazelcast支持通過主題進(jìn)行消息的可靠分發(fā)。
-
并發(fā)工具
- FencedLock:java 中
java.util.concurrent.locks.Lock的分布式實(shí)現(xiàn),可以保證集群中只有一個(gè)線程可以獲得鎖。 - ISemaphore:java中
java.util.concurrent.Semaphore的分布式實(shí)現(xiàn)。 - IAtomicLong :java中
java.util.concurrent.atomic.AtomicLong的分布式實(shí)現(xiàn)。 - IAtomicReference:Java中
java.util.concurrent.atomic.AtomicReference的分布式實(shí)現(xiàn)。 - FlakeIdGenerator:用于生產(chǎn)集群范圍內(nèi)的唯一標(biāo)識(shí)符。
- ICountdownLatch: Java中
java.util.concurrent.CountDownLatch的分布式實(shí)現(xiàn)。 - PN counter:一個(gè)分布式數(shù)據(jù)結(jié)構(gòu),其中每個(gè)Hazelcast實(shí)例都可以遞增和遞減計(jì)數(shù)器值,并將這些更新傳播到所有副本。
- FencedLock:java 中
Event Journal:是一種分布式數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)map或緩存上操作的歷史記錄。
1. 分布式對(duì)象簡(jiǎn)介
基于分區(qū)策略,Hazelcast有兩種類型的分布式數(shù)據(jù)結(jié)構(gòu):分區(qū)數(shù)據(jù)結(jié)構(gòu)和非分區(qū)數(shù)據(jù)結(jié)構(gòu),分區(qū)數(shù)據(jù)結(jié)構(gòu)一個(gè)分區(qū)只保存部分?jǐn)?shù)據(jù),非分區(qū)數(shù)據(jù)結(jié)構(gòu)一個(gè)分區(qū)保存全部的數(shù)據(jù)。
Hazelcast中的分區(qū)數(shù)據(jù)結(jié)構(gòu)包括以下四種:
- Map
- MultiMap
- Cache
- Event Journal
以下為非分區(qū)數(shù)據(jù)結(jié)構(gòu):
- Queue
- Set
- List
- Ringbuffer
- FencedLock
- ISemaphore
- IAtomicLong
- IAtomicReference
- FlakeldGenerator
- ICountdownLatch
- Cardinality Estimator
- PN Counter
除分區(qū)和非分區(qū)數(shù)據(jù)結(jié)構(gòu)以外,Hazelcast同時(shí)提供了Replicated Map(可復(fù)制集合)數(shù)據(jù)結(jié)構(gòu)。
2 . 分布式對(duì)象的加載和銷毀
針對(duì)大多數(shù)分布式對(duì)象,Hazelcast提供了獲取實(shí)例的get 方法。一個(gè)分布式對(duì)象的加載,首先需要?jiǎng)?chuàng)建一個(gè)Hazelcast實(shí)例,然后通過Hazelcast實(shí)例調(diào)用對(duì)應(yīng)的get 方法獲取。下面的例子創(chuàng)建一個(gè)Hazelcast實(shí)例instance1 并在該實(shí)例上創(chuàng)建一個(gè)叫data的分布式Map。
HazelcastInstance instance1 = Hazelcast.newHazelcastInstance();
IMap<String, String> data = instance1.getMap("data");
Hazelcast支持對(duì)分布式對(duì)象的屬性進(jìn)行配置,默認(rèn)使用hazelcast.xml中的配置信息,也可以在xml中顯示配置或根據(jù)需求使用代碼進(jìn)行配置。Hazelcast中的大多數(shù)分布式對(duì)象都是懶加載,只有在第一次操作對(duì)象時(shí)才創(chuàng)建對(duì)象。使用已經(jīng)創(chuàng)建的對(duì)象,可以直接通過對(duì)象的引用訪問,無需重新加載一次。
銷毀分布式對(duì)象可以使用destory 方法,該方法會(huì)清理和釋放對(duì)象的所有資源。使用該方法時(shí)需要十分謹(jǐn)慎,銷毀對(duì)象并創(chuàng)建一個(gè)新的對(duì)象賦值給原來的對(duì)象引用不會(huì)產(chǎn)生任何錯(cuò)誤。下面的代碼展示了這種潛在的危險(xiǎn)。
HazelcastInstance instance1 = Hazelcast.newHazelcastInstance();
IMap<String, String> data = instance1.getMap("data");
data.put("hazelcast","a good tool");
System.out.println("The size of map = " + data.size());
data.destroy();
System.out.println("The size of map = " + data.size());
上述代碼運(yùn)行沒有任何錯(cuò)誤,輸出結(jié)果如下:
The size of map = 1
The size of map = 0
Hazelcast中所有的分布式數(shù)據(jù)結(jié)構(gòu)都被設(shè)計(jì)為當(dāng)訪問對(duì)象的時(shí)刻才創(chuàng)建對(duì)象,因此即便已經(jīng)銷毀了對(duì)象,只要對(duì)該對(duì)象還有訪問,對(duì)象就會(huì)被重新創(chuàng)建。
3. 控制分區(qū)
Hazelcast使用分布式對(duì)象的名字決定該對(duì)象應(yīng)該存儲(chǔ)在哪個(gè)分區(qū)。下面創(chuàng)建兩個(gè)Queue 對(duì)象,名字分別為q1和q2:
HazelcastInstance instance1 = Hazelcast.newHazelcastInstance();
IQueue<String> queue1 = instance1.getQueue("q1");
IQueue<String> queue2 = instance1.getQueue("q2");
由于queue1和queue2兩個(gè)隊(duì)列的名字不同,因此他們將會(huì)存儲(chǔ)在不同的分區(qū)。如果想將兩個(gè)隊(duì)列存儲(chǔ)在同一個(gè)分區(qū),可以使用@ 符設(shè)置分區(qū)key,從而將兩個(gè)隊(duì)列存儲(chǔ)在同一個(gè)分區(qū):
HazelcastInstance instance1 = Hazelcast.newHazelcastInstance();
IQueue<String> queue1 = instance1.getQueue("q1@test");
IQueue<String> queue2 = instance1.getQueue("q2@test");
現(xiàn)在兩個(gè)隊(duì)列存儲(chǔ)在同一個(gè)分區(qū),因?yàn)樗鼈兪褂孟嗤姆謪^(qū)key :test,Hazelcast提供了getPartitionKey方法用于獲取分區(qū)key的信息,這將有助于創(chuàng)建一個(gè)和已有對(duì)象存儲(chǔ)在同一個(gè)分區(qū)的新對(duì)象。
HazelcastInstance instance1 = Hazelcast.newHazelcastInstance();
IQueue<String> queue1 = instance1.getQueue("q1@test");
IQueue<String> queue2 = instance1.getQueue("q2@test");
System.out.println("queue1 partition key = " + queue1.getPartitionKey());
System.out.println("queue2 partition key = " + queue2.getPartitionKey());
上面的代碼將會(huì)輸出queue1和queue2的分區(qū)key:
queue1 partition key = test
queue2 partition key = test
4. 公共特性
Hazelcast中所有分布式對(duì)象都具有高可用性(HA):
- 當(dāng)集群中有一個(gè)成員發(fā)生故障時(shí),保存相同數(shù)據(jù)的備份副本會(huì)將包括權(quán)限、鎖在內(nèi)的所有數(shù)據(jù)分配給集群內(nèi)其他成員,無數(shù)據(jù)丟失。
- 集群中所有的成員都有相同的權(quán)力和責(zé)任,沒有主節(jié)點(diǎn)或leader,不依賴外部服務(wù),因此沒有單點(diǎn)故障。
5. 樣例
下面的代碼將簡(jiǎn)單的展示如何獲取已經(jīng)創(chuàng)建的分布式對(duì)象實(shí)例以及如何監(jiān)聽實(shí)例事件:
HazelcastInstance instance = Hazelcast.newHazelcastInstance();
instance.addDistributedObjectListener(new DistributedObjectListener() {
@Override
public void distributedObjectCreated(DistributedObjectEvent event) {
DistributedObject instance = event.getDistributedObject();
System.out.println(instance.getName() + " created");
}
@Override
public void distributedObjectDestroyed(DistributedObjectEvent event) {
System.out.println(event.getObjectName() + " destroyed");
}
});
IQueue<String> queue1 = instance.getQueue("q1");
IQueue<String> queue2 = instance.getQueue("q2");
queue1.add("hello");
queue2.add("world");
instance.getDistributedObjects().forEach(o -> System.out.println(o.getName()));
instance.getDistributedObjects().forEach(DistributedObject::destroy);
上面的樣例代碼將輸出:
q1 created
q2 created
q1
q2
q1 destroyed
q2 destroyed