disgear是筆者參考solrcloud架構基於redis實現的分佈式的緩存,支持數據切分到多臺機器上,支持HA,支持讀寫分離和主節點失效自動選舉,目前把它開放到github上,開放給你們node
github:https://github.com/yangbutao/disgeargit
內存操做,讀寫性能要求比較高github
支持數據切分,分爲多個Shard,每一個shard負責必定範圍的數據redis
當單個節點的數據量比較大的時,能夠對該節點進行數據切分,分離成兩部分,增長新的機器便可。算法
系統不存在單點問題,緩存節點支持HA,master節點宕掉後,自動選舉一個slave爲主節點,對客戶端透明json
首先啓動好全部的redis實例,每一個redis實例有一個agent負責監控,agent ping redis實例的狀態,agent 鏈接到zookeeper上建立/collections/collection1/leader_elect/shard1/election下的節點,監控比它小的節點,變化時更新leader節點。api
當agent鏈接zookeeper超時或者agent ping redis實例超時,會刪除zookeeper上的election下的節點(agent ping redis超時,調用zookeeper api刪除節點)。緩存
當ping到redis節點起來後,若檢查到zookeeper上沒有對應節點,則建立相應節點。bash
Leader(master)是緩存負責響應的節點,replica(slave)只負責同步leader的數據,當leader宕機後,提高replica爲主節點。架構
如下是zookeeper上節點的相關說明:
/collections
|----collection1
|----leader_elect
----shard1[0-7ffffffff] //節點啓動時,一共須要分幾個 分區,須要聲明該節點屬於哪一個shard
---election //參與選舉的節點列表,競爭成爲leader節 點,並更新leaders節點列表和clusterstate.json節點內容,並調用腳 本 更改redis的master slave配置
----10.1.1.21
----10.1.1.22
----shard2
---eclection
---10.1.1.23
---10.1.1.24
|---leaders
----shard1 //當前shard的leader節點
---10.1.1.21
-----shard2
----10.1.1.24
/observer //負責watch collections中節點的變化,
由leader節點更新 clusterstate.json節點的內容,observer節點須要在系統初始 化過程當中最新選舉出leader節點,作watch collections下的 節點的變化。
|---election //其下是供選擇的節點
|----leader //這樣一個好處是每一個client節點無需對多個zookeeper的節點監控計算,由單獨的節點對這些變化的節點作監控, 來更新集羣狀態,這樣每一個client只須要 watch 集羣狀態 內容zookeeper節點便可。
/live_nodes
|---10.1.1.21
|----10.1.1.22
|----10.1.1.23
|-----10.1.1.24
/clusterstate.json //集羣狀態,當有節點變化時,更新這個節點 內容,每一個client端監聽該節點內容的變化, 緩存在本地cache中。
相關內容以下:
{collection1={
"shards":{
"shard1":{
"range":"80000000-ffffffff",
「leader」:node1,
"replicas":{
"node1":{
"state":"alive", "node_name":"192.168.1.21:8983"},
"node2":{
"state":"alive",
"node_name":"192.168.1.22:8983"
}}},
"shard2":{
"range":"0-7fffffff",
"state":"active",
「leader」:node3,
"replicas":{
"node3":{
"node_name":"192.168.1.23:8983"},
"core_node4":{
"node_name":"192.168.1.24:8983"
}}},
對於數據切分,數據的範圍取Integer.MIN_VALUE, Integer.MAX_VALUE,首次初始化時,根據規劃的Shard的數量,平均進行切分,每一個Shard負責一個固定的數據範圍,好比Shard1[0-7fffffff],這些Shard和數據的範圍會持久化到zookeeper節點上,以便於集羣重啓後,從zookeeper本地拉起Shard劃分和數據範圍的相關數據,以下圖所示。
/collections
|----collection1
|----leader_elect
----shard1[0-7ffffffff]
----shard2[800000-9ffffffff]
當須要擴容緩存節點時,管理員能夠對Shard進行split操做,生成兩個新的Shard,每一個Shard負責一部分數據,註冊在zooleeper上的/collections/collection1/leader_elect節點下。好比Shard1[0-7ffffffff]通過split操做後,變成Shard1_1[0-3ffffffff]和Shard1_2[400000-7fffffffff]。
/collections
|----collection1
|----leader_elect
----shard1[0-7ffffffff]
----shard2[800000-9ffffffff]
----shard1_1[0-3ffffffff]
----shard1_2[0-3ffffffff]
新的節點加入到新的Shard分區中,完成leader選舉等一系列操做後,更新集羣拓撲狀態,並刪除老的Shard分區,這樣新的Shard就處於可用狀態。
對於key的hash定位,採用murmurhash3_x86_32 算法,效率比較高,而後判斷該hash值落在哪一個shard的數據範圍內,進行數據的分區定位。
Agent做爲緩存節點的代理,一方面和zookeeper進行通訊,把緩存節點的狀態通知給zookeeper,另一方面對redis緩存節點作管理和監控。
須要在Agent中調用如下腳本(monitor_redis.sh),把返回的結果,通知給zookeeper
ALIVE=$(/opt/cache/redis-2.6.16/src/redis-cli -h 10.1.1.25 -p 6379 -a 123 PING)
echo $ALIVE
當節點上的agent在zookeeper上成爲leader節點時,須要通知agent把它代理的redis變動爲master節點,agent調用如下腳本。
主節點的腳本(redis_master.sh):
#!/bin/bash
/opt/cache/redis-2.6.16/src/redis-cli -h 10.1.1.26 -p 6379 slaveof NO ONE
當節點上的agent在zookeeper上成爲replica節點時,須要通知agent把它代理的redis變動爲slave節點,agent調用如下腳本。
slave節點腳本(redis_slaves.sh):
#!/bin/bash
/opt/cache/redis-2.6.16/src/redis-cli -h 10.1.1.26 -p 6379 slaveof 10.1.1.25 6379
Observer負責對整個集羣collection下的Shard節點的變更進行監聽,更新zookeeper上的集羣狀態節點cloudstate.json。
Agent節點在啓動過程當中,首選要作的就是Observer的選舉。
Observer須要由zookeeper選舉出來後,監聽collections下節點的變化,如有變化則進行處理process,計算的結果設置到zookeeper的cloudstate.json節點。
當cloudstate.json節點數據變化時,負責監聽該節點的全部客戶端都更新本地客戶端內存。
/observer
|---election
----10.1.1.21
----10.1.1.22
-----10.1.1.23
|----leader
-----10.1.1.22
Shard節點在zookeeper上建立完成後,緩存節點在啓動的過程當中,須要經過命令行-D參數或者配置文件的形式加入到集羣中的某一個Shard分區下,參與leader的選舉,具體實現是爲每一個節點生成一個數字,每一個節點只須要監控比本身小的節點便可,若有變化,則進行通知,並使得數字最小的節點成爲leader節點。
/collections
|----collection1
|----leader_elect
----shard1 //節點啓動時,一共須要分幾個分區,須要 聲明該節點屬於哪一個shard
---eclection //參與選舉的節點列表,競爭成爲leader節 點,並更新leaders節點列表和clusterstate.json節點內容,並調用腳 本 更改redis的master slave配置
----10.1.1.21
----10.1.1.22
----shard2
---eclection
---10.1.1.23
---10.1.1.24
|---leaders
----shard1 //當前shard的leader節點
---10.1.1.21
-----shard2
----10.1.1.24
當某shard的訪問壓力過大或者數據量比較大時候,節點的擴展有兩種方式,
一種是增長shard中的節點數量,提升讀的能力,這種方式已經實現;
另一種方式對壓力大的shard作split的操做,一個shard分割爲兩個shard,提升shard的寫數據的能力,這種節點的擴展方式目前還不支持,已歸入到後續的計劃中。
Agent的啓動腳本:
MAIN="com.newcosoft.cache.agent.AgentMain"
$JAVA $JAVA_OPTS -cp "$CLASSPATH" $JVMFLAGS $MAIN -Dcol=col1 -Dshard=shard1 -Dnode=node25 \
-DbaseUrl=10.1.1.25:6379 -DscriptPath=/opt/lsmp -DshardNum=3 -Dzk_url=10.1.1.25:2181
其中-Dcol表示當前建立的collection名稱,對於redis來說,表明要存儲庫的邏輯名稱
-Dshard表示當前的節點的shard名稱
-Dnode表示當前節點的邏輯名稱
-DbaseUrl表示redis的主機IP和端口號
-DscriptPath表示redis監控和操做相關的腳本的路徑,腳本有monitor_redis.sh、redis_master.sh、redis_slaves.sh
-DshardNum表示當前collection中的shard數目
-Dzk_url表明zookeeper集羣的地址
Client端的使用:
在使用disgear的client端啓動初始化中,引用disgear相關的jar,並調用
com.newcosoft.client.ClusterStateCacheManager.INSTANCE=new ClusterStateCacheManager(zkUrl);
ClusterStateCacheManager.INSTANCE.createClusterStateWatcher();
建立clusterState的監聽器。
這樣後續經過com.newcosoft.client.ClusterStateCacheManager.INSTANCE.getClusterState()就能夠得到當前集羣的節點狀態,
進而選擇合適的節點進行分發數據存取請求。