[Hazelcast系列 三]分布式數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)介

Hazelcast提供了很多通用數(shù)據(jù)結(jié)構(gòu)的分布式實(shí)現(xiàn)。針對(duì)每一種不同的客戶端語言,Hazelcast都盡量模擬該語言原生接口,對(duì)于Java客戶端,Hazelcast提供的IMapjava.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)。
  • 主題

    主題是用于多個(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ù)器值,并將這些更新傳播到所有副本。
  • 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ì)象,名字分別為q1q2

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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容