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()就能够得到当前集群的节点状态,
进而选择合适的节点进行分发数据存取请求。