hazelcast其中一個很重要的應用就是能夠將多個應用服務器組成一個分佈式環境的應用,造成一個cluster。這個cluster能夠選舉出一個master來對外工做。而cluster中的各臺服務器之間有數據同步機制和數據備份機制,來避免由於單個節點掛掉而致使數據丟失和cluster的失效。數據存儲在分佈式內存中,hazelcast能夠保證數據在各個節點的均勻分佈,能夠增長節點和減小節點,而這個過程當中,hazelcast會自動遷移數據,來保證數據的高可用。html
在hazelcast中,master是指集羣裏面的主節點。這個主節點負責的是向其餘成員節點更新最新的成員列表。算法
一開始尚未節點,第一個啓動的節點會先尋找其餘節點,這裏會根據配置的尋找機制,若是是multicast,就用multicast的方式尋找。若是是TCP/IP,就用TCP/IP的方式尋找。 找不到,就選舉本身爲master。數據庫
後面加入的節點,會向已存在cluster的master發出join請求,master檢測是否符合集羣的加入要求,若是符合,就會發送最新的成員列表給新加入的成員。同時更新集羣中的數據,保證數據的均勻分佈和冗餘。服務器
集羣中的每一個成員都有相同的成員列表。成員列表是按加入的順序排好的。若是master 掛掉了,那麼剩下的成員會收到通知,而後從成員列表中選舉下一個做爲master。網絡
在可擴展的動態分區存儲系統中,不管是NoSQL數據庫、文件系統或者是內存數據網格,在集羣更改時(添加或者刪除節點),在集 羣從新均衡時可能致使網絡大數據傳輸。須要從新均衡這些節點中的主從數據。例如一個節點掛了,掛掉的節點的主備數據必須從新分配給其餘在線的節點。以MB 數據傳輸會對集羣形成消極的影響,由於要消耗機器的寶貴資源(例如網絡流量、CPU和RAM)。在此期間可能致使嚴重的操做延時。異步
假設集羣中有50個節點,每一個存儲40GB數據(20GB主數據和20GB備份數據);假設節點5掛了,咱們必須保證節點5的存 儲在集羣中的臨近節點中。那麼節點5到的主備數據將被分配到臨近節點7。這意味着節點7的數據量是其餘節點的2倍。也意味着節點7消耗更多的CPU來處理 這兩倍請求。另外節點7必須備份這個20GB的數據到其餘節點9。這對這兩個節點的負載形成極壞的影響。節點9也須要更多的內存去存儲這20GB的備份數 據。形成大量的內存浪費。分佈式
Hazelcast 解決了以上問題。一個節點的主數據均勻地被分到其餘節點。也就是說每一個 節點都負責備份其餘節點主數據。這樣會有更好地使用內存和在減少添加或者刪除結點時對集羣的影響。假設集羣中有50個節點要存儲2TB的數據;每一個節點存 儲20G主數據和20G備數據。假設節點3的20GB的主數據被備份到其餘49個節點,每一個節點有20GB/49節點3的主數據。若是節點3掛了,每一個節 點有1/49它的數據;注意沒有任何數據遷移而且集羣依然是均衡的!這樣的備份機制在節點掛了以後是不須要馬上從新均衡的。假設你添加了5個節點。集羣中也沒有立馬均衡;由於存在的節點已經在最佳狀態。Hazelcast 會很溫柔地遷移一些數據到這些新的節點,最終數據均勻存儲。性能
Operation是hazelcast裏面操做邏輯的封裝。操做的邏輯要放在run方法裏面,相似於runnable。大數據
public static void main(String[] args) { ClientConfig clientConfig = new ClientConfig(); clientConfig.addAddress("10.10.4.40:5701"); // client初始化時會建立一系列service(線程池管理器、集羣客戶端服務、虛擬節點管理、動態擴展服務等),先啓動ClientClusterServiceImpl,讀取當前活動的實際節點(先根據clientConfig指定的地址獲取connection,而後基於這個鏈接,再發起讀取實際節點的請求),而後啓動ClientPartitionServiceImpl,向各個實際活動節點發起請求獲取其上的虛擬節點,記錄到一個ConcurrentHashMap裏。 HazelcastInstance instance = HazelcastClient.newHazelcastClient(clientConfig); // 這裏的map並非真的map,而是一個mapProxy // 而且這裏要指定key和value的類型 MapmapCustomers = instance.getMap("customers"); // put時,由這個mapProxy先把key和value都序列化爲byte[] // 而後用key的hash對虛擬節點數取餘:key.getHash()%271,得到partitionId // 根據ClientPartitionServiceImpl裏的ConcurrentHashMap記錄的虛擬節點和實際節點的對應關係,肯定了該key對應的實際節點。而後經過BufferedOutputStream方式 對該地址發起操做請求。 mapCustomers.put(1, "Joe"); // 跟put相似,定位節點,而後發請求。只是請求類型不一樣而已。 System.out.println("Customer with key 1: "+ mapCustomers.get(1)); System.out.println("Map Size:" + mapCustomers.size()); } 複製代碼
首先當 ClientClusterServiceImpl啓動以後就產生一個一直監聽節點存活狀況的線程[cluster-listener]。
那麼假設在執行 mapCustomers.put(1, "Joe");操做前,其要操做的實際節點掛了,這時[cluster-listener]線程會當即感知並更新虛擬節點表。 若是在更新完畢後操做才發出請求,則操做能夠成功,若是在更新未完成時發出了請求,則會拋出異常。而這個更新虛擬節點表的過程須要幾秒鐘。在這幾秒鐘裏對失效節點的操做就真的失敗了。spa
客戶端向 Hazelcast寫入數據本體所在節點是必須同步的;而備份過程默認是同步的,也能夠修改配置成異步。 爲了保證一致性,默認狀況下,讀取數據老是從數據的owner節點讀取,這個也能夠修改配置成容許從備份節點讀數據,這樣能帶來更好的讀性能。
舉例來講, 要更新key爲1的數據時,由一致性hash算法得知其存在節點A上,則對節點A發起update請求,這時若是你用另外一個客戶端也要更新節點A上的key1時,這兩個操做確定是同步控制的。而節點A把key1備份到節點B的過程也能夠配成同步,而後再配成容許從備份節點讀取,這樣保證了一致性和高可讀。若是備份過程配成異步,再配成不容許從備份節點讀取,則保證了高可寫,而一致性也基本ok,只是萬一異步備份未完成時,數據本體所在節點掛掉,那就可能產生髒數據。
========廣告時間========
鄙人的新書《Tomcat內核設計剖析》已經在京東銷售了,有須要的朋友能夠到 item.jd.com/12185360.ht… 進行預約。感謝各位朋友。
=========================
歡迎關注: