到 目前 为止,咱们所学习的 Redis 都是 单机版 的,这也就意味着一旦咱们所依赖的 Redis 服务宕机了,咱们的主流程也会受到必定的影响,这固然是咱们不可以接受的。html
因此一开始咱们的想法是:搞一台备用机。这样咱们就能够在一台服务器出现问题的时候切换动态地到另外一台去:node
幸运的是,两个节点数据的同步咱们可使用 Redis 的 主从同步 功能帮助到咱们,这样一来,有个备份,内心就踏实多了。git
后来由于某种神秘力量,Redis 老会在莫名其妙的时间点出问题 (好比半夜 2 点),我总不能 24 小时时刻守在电脑旁边切换节点吧,因而另外一个想法又开始了:给全部的节点找一个 "管家",自动帮我监听照顾节点的状态并切换:程序员
这大概就是 Redis 哨兵 (Sentinel) 的简单理解啦。什么?管家宕机了怎么办?相较于有大量请求的 Redis 服务来讲,管家宕机的几率就要小得多啦.. 若是真的宕机了,咱们也能够直接切换成当前可用的节点保证可用..github
好了,经过上面的一些解决方案咱们对 Redis 的 稳定性 稍微有了一些底气了,但单台节点的计算能力始终有限,所谓人多力量大,若是咱们把 多个节点组合 成 一个可用的工做节点,那就大大增长了 Redis 的 高可用、可扩展、分布式、容错 等特性:web
主从复制,是指将一台 Redis 服务器的数据,复制到其余的 Redis 服务器。前者称为 主节点(master),后者称为 从节点(slave)。且数据的复制是 单向 的,只能由主节点到从节点。Redis 主从复制支持 主从同步 和 从从同步 两种,后者是 Redis 后续版本新增的功能,以减轻主节点的同步负担。redis
在 Redis 中,用户能够经过执行 SLAVEOF
命令或者设置 slaveof
选项,让一个服务器去复制另外一个服务器,如下三种方式是 彻底等效 的:算法
slaveof <masterip> <masterport>
--slaveof <masterip> <masterport>
slaveof <masterip> <masterport>
,让该 Redis 实例成为从节点。
须要注意的是:主从复制的开启,彻底是在从节点发起的,不须要咱们在主节点作任何事情。数组
在正确安装好 Redis 以后,咱们可使用 redis-server --port <port>
的方式指定建立两个不一样端口的 Redis 实例,例如,下方我分别建立了一个 6379
和 6380
的两个 Redis 实例:安全
# 建立一个端口为 6379 的 Redis 实例
redis-server --port 6379
# 建立一个端口为 6380 的 Redis 实例
redis-server --port 6380
复制代码
此时两个 Redis 节点启动后,都默认为 主节点。
咱们在 6380
端口的节点中执行 slaveof
命令,使之变为从节点:
# 在 6380 端口的 Redis 实例中使用控制台
redis-cli -p 6380
# 成为本地 6379 端口实例的从节点
127.0.0.1:6380> SLAVEOF 127.0.0.1ø 6379
OK
复制代码
下面咱们来验证一下,主节点的数据是否会复制到从节点之中:
127.0.0.1:6380> GET mykey
(nil)
复制代码
127.0.0.1:6379> SET mykey myvalue
OK
复制代码
127.0.0.1:6380> GET mykey
"myvalue"
复制代码
经过 slaveof <masterip> <masterport>
命令创建主从复制关系之后,能够经过 slaveof no one
断开。须要注意的是,从节点断开复制后,不会删除已有的数据,只是再也不接受主节点新的数据变化。
从节点执行 slaveof no one
以后,从节点和主节点分别打印日志以下:、
# 从节点打印日志
61496:M 17 Mar 2020 08:10:22.749 # Connection with master lost.
61496:M 17 Mar 2020 08:10:22.749 * Caching the disconnected master state.
61496:M 17 Mar 2020 08:10:22.749 * Discarding previously cached master state.
61496:M 17 Mar 2020 08:10:22.749 * MASTER MODE enabled (user request from 'id=4 addr=127.0.0.1:55096 fd=8 name= age=1664 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=34 qbuf-free=32734 obl=0 oll=0 omem=0 events=r cmd=slaveof')
# 主节点打印日志
61467:M 17 Mar 2020 08:10:22.749 # Connection with replica 127.0.0.1:6380 lost.
复制代码
为了节省篇幅,我把主要的步骤都 浓缩 在了上图中,其实也能够 简化成三个阶段:准备阶段-数据同步阶段-命令传播阶段。下面咱们来进行一些必要的说明。
在上面的 快速体验 过程当中,你会发现 slaveof
这个命令竟然不须要验证?这意味着只要知道了 ip 和端口就能够随意拷贝服务器上的数据了?
那固然不可以了,咱们能够经过在 主节点 配置 requirepass
来设置密码,这样就必须在 从节点 中对应配置好 masterauth
参数 (与主节点 requirepass
保持一致) 才可以进行正常复制了。
每次执行 SYNC
命令,主从服务器须要执行以下动做:
BGSAVE
命令来生成 RDB 文件,这个生成操做会
消耗 主服务器大量的
CPU、内存和磁盘 I/O 的资源;
特别是当出现 断线重复制 的状况是时,为了让从服务器补足断线时确实的那一小部分数据,却要执行一次如此耗资源的 SYNC
命令,显然是不合理的。
因此在 Redis 2.8 中引入了 PSYNC
命令来代替 SYNC
,它具备两种模式:
部分复制的原理主要是靠主从节点分别维护一个 复制偏移量,有了这个偏移量以后断线重连以后一比较,以后就能够仅仅把从服务器断线以后确实的这部分数据给补回来了。
更多的详细内容能够参考下方 参考资料 3
上图 展现了一个典型的哨兵架构图,它由两部分组成,哨兵节点和数据节点:
在复制的基础上,哨兵实现了 自动化的故障恢复 功能,下方是官方对于哨兵功能的描述:
其中,监控和自动故障转移功能,使得哨兵能够及时发现主节点故障并完成转移。而配置提供者和通知功能,则须要在与客户端的交互中才能体现。
正确安装好 Redis 以后,咱们去到 Redis 的安装目录 (mac 默认在 /usr/local/
),找到 redis.conf
文件复制三份分别命名为 redis-master.conf
/redis-slave1.conf
/redis-slave2.conf
,分别做为 1
个主节点和 2
个从节点的配置文件 (下图演示了我本机的 redis.conf
文件的位置)
打开能够看到这个 .conf
后缀的文件里面有不少说明的内容,所有删除而后分别改为下面的样子:
#redis-master.conf
port 6379
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"
#redis-slave1.conf
port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
slaveof 127.0.0.1 6379
#redis-slave2.conf
port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 127.0.0.1 6379
复制代码
而后咱们能够执行 redis-server <config file path>
来根据配置文件启动不一样的 Redis 实例,依次启动主从节点:
redis-server /usr/local/redis-5.0.3/redis-master.conf
redis-server /usr/local/redis-5.0.3/redis-slave1.conf
redis-server /usr/local/redis-5.0.3/redis-slave2.conf
复制代码
节点启动后,咱们执行 redis-cli
默认链接到咱们端口为 6379
的主节点执行 info Replication
检查一下主从状态是否正常:(能够看到下方正确地显示了两个从节点)
按照上面一样的方法,咱们给哨兵节点也建立三个配置文件。(哨兵节点本质上是特殊的 Redis 节点,因此配置几乎没什么差异,只是在端口上作区分就好)
# redis-sentinel-1.conf
port 26379
daemonize yes
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 6379 2
# redis-sentinel-2.conf
port 26380
daemonize yes
logfile "26380.log"
sentinel monitor mymaster 127.0.0.1 6379 2
# redis-sentinel-3.conf
port 26381
daemonize yes
logfile "26381.log"
sentinel monitor mymaster 127.0.0.1 6379 2
复制代码
其中,sentinel monitor mymaster 127.0.0.1 6379 2
配置的含义是:该哨兵节点监控 127.0.0.1:6379
这个主节点,该主节点的名称是 mymaster
,最后的 2
的含义与主节点的故障断定有关:至少须要 2
个哨兵节点赞成,才能断定主节点故障并进行故障转移。
执行下方命令将哨兵节点启动起来:
redis-server /usr/local/redis-5.0.3/redis-sentinel-1.conf --sentinel
redis-server /usr/local/redis-5.0.3/redis-sentinel-2.conf --sentinel
redis-server /usr/local/redis-5.0.3/redis-sentinel-3.conf --sentinel
复制代码
使用 redis-cil
工具链接哨兵节点,并执行 info Sentinel
命令来查看是否已经在监视主节点了:
# 链接端口为 26379 的 Redis 节点
➜ ~ redis-cli -p 26379
127.0.0.1:26379> info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
复制代码
此时你打开刚才写好的哨兵配置文件,你还会发现出现了一些变化:
首先,咱们使用 kill -9
命令来杀掉主节点,同时 在哨兵节点中执行 info Sentinel
命令来观察故障节点的过程:
➜ ~ ps aux | grep 6379
longtao 74529 0.3 0.0 4346936 2132 ?? Ss 10:30上午 0:03.09 redis-server *:26379 [sentinel]
longtao 73541 0.2 0.0 4348072 2292 ?? Ss 10:18上午 0:04.79 redis-server *:6379
longtao 75521 0.0 0.0 4286728 728 s008 S+ 10:39上午 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn 6379
longtao 74836 0.0 0.0 4289844 944 s006 S+ 10:32上午 0:00.01 redis-cli -p 26379
➜ ~ kill -9 73541
复制代码
若是 刚杀掉瞬间 在哨兵节点中执行 info
命令来查看,会发现主节点尚未切换过来,由于哨兵发现主节点故障并转移须要一段时间:
# 第一时间查看哨兵节点发现并未转移,还在 6379 端口
127.0.0.1:26379> info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
复制代码
一段时间以后你再执行 info
命令,查看,你就会发现主节点已经切换成了 6381
端口的从节点:
# 过一段时间以后在执行,发现已经切换了 6381 端口
127.0.0.1:26379> info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6381,slaves=2,sentinels=3
复制代码
但同时还能够发现,哨兵节点认为新的主节点仍然有两个从节点 (上方 slaves=2),这是由于哨兵在将 6381
切换成主节点的同时,将 6379
节点置为其从节点。虽然 6379
从节点已经挂掉,可是因为 哨兵并不会对从节点进行客观下线,所以认为该从节点一直存在。当 6379
节点从新启动后,会自动变成 6381
节点的从节点。
另外,在故障转移的阶段,哨兵和主从节点的配置文件都会被改写:
slaveof
配置的变化,新的主节点没有了
slaveof
配置,其从节点则
slaveof
新的主节点。
上面咱们在 快速体验 中主要感觉到了服务端本身对于当前主从节点的自动化治理,下面咱们以 Java 代码为例,来演示一下客户端如何访问咱们的哨兵系统:
public static void testSentinel() throws Exception {
String masterName = "mymaster";
Set<String> sentinels = new HashSet<>();
sentinels.add("127.0.0.1:26379");
sentinels.add("127.0.0.1:26380");
sentinels.add("127.0.0.1:26381");
// 初始化过程作了不少工做
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels);
Jedis jedis = pool.getResource();
jedis.set("key1", "value1");
pool.close();
}
复制代码
Jedis 客户端对哨兵提供了很好的支持。如上述代码所示,咱们只须要向 Jedis 提供哨兵节点集合和 masterName
,构造 JedisSentinelPool
对象,而后即可以像使用普通 Redis 链接池同样来使用了:经过 pool.getResource()
获取链接,执行具体的命令。
在整个过程当中,咱们的代码不须要显式的指定主节点的地址,就能够链接到主节点;代码中对故障转移没有任何体现,就能够在哨兵完成故障转移后自动的切换主节点。之因此能够作到这一点,是由于在 JedisSentinelPool
的构造器中,进行了相关的工做;主要包括如下两点:
masterName
得到主节点的信息;该功能是经过调用哨兵节点的
sentinel get-master-addr-by-name
命令实现;
故障转移操做的第一步 要作的就是在已下线主服务器属下的全部从服务器中,挑选出一个状态良好、数据完整的从服务器,而后向这个从服务器发送 slaveof no one
命令,将这个从服务器转换为主服务器。可是这个从服务器是怎么样被挑选出来的呢?
简单来讲 Sentinel 使用如下规则来选择新的主服务器:
上图 展现了 Redis Cluster 典型的架构图,集群中的每个 Redis 节点都 互相两两相连,客户端任意 直连 到集群中的 任意一台,就能够对其余 Redis 节点进行 读写 的操做。
Redis 集群中内置了 16384
个哈希槽。当客户端链接到 Redis 集群以后,会同时获得一份关于这个 集群的配置信息,当客户端具体对某一个 key
值进行操做时,会计算出它的一个 Hash 值,而后把结果对 16384
求余数,这样每一个 key
都会对应一个编号在 0-16383
之间的哈希槽,Redis 会根据节点数量 大体均等 的将哈希槽映射到不一样的节点。
再结合集群的配置信息就可以知道这个 key
值应该存储在哪个具体的 Redis 节点中,若是不属于本身管,那么就会使用一个特殊的 MOVED
命令来进行一个跳转,告诉客户端去链接这个节点以获取数据:
GET x
-MOVED 3999 127.0.0.1:6381
复制代码
MOVED
指令第一个参数 3999
是 key
对应的槽位编号,后面是目标节点地址,MOVED
命令前面有一个减号,表示这是一个错误的消息。客户端在收到 MOVED
指令后,就当即纠正本地的 槽位映射表,那么下一次再访问 key
时就可以到正确的地方去获取了。
bgsave
和
bgrewriteaof
的
fork
操做可能致使主进程阻塞,主从环境下主机切换时可能致使从节点长时间没法提供服务,全量复制阶段主节点的复制缓冲区可能溢出……
首先咱们找一个地方建立一个名为 redis-cluster
的目录:
mkdir -p ~/Desktop/redis-cluster
复制代码
而后按照上面的方法,建立六个配置文件,分别命名为:redis_7000.conf
/redis_7001.conf
.....redis_7005.conf
,而后根据不一样的端口号修改对应的端口值就行了:
# 后台执行
daemonize yes
# 端口号
port 7000
# 为每个集群节点指定一个 pid_file
pidfile ~/Desktop/redis-cluster/redis_7000.pid
# 启动集群模式
cluster-enabled yes
# 每个集群节点都有一个配置文件,这个文件是不能手动编辑的。确保每个集群节点的配置文件不通
cluster-config-file nodes-7000.conf
# 集群节点的超时时间,单位:ms,超时后集群会认为该节点失败
cluster-node-timeout 5000
# 最后将 appendonly 改为 yes(AOF 持久化)
appendonly yes
复制代码
记得把对应上述配置文件中根端口对应的配置都修改掉 (port/ pidfile/ cluster-config-file)。
redis-server ~/Desktop/redis-cluster/redis_7000.conf
redis-server ~/Desktop/redis-cluster/redis_7001.conf
redis-server ~/Desktop/redis-cluster/redis_7002.conf
redis-server ~/Desktop/redis-cluster/redis_7003.conf
redis-server ~/Desktop/redis-cluster/redis_7004.conf
redis-server ~/Desktop/redis-cluster/redis_7005.conf
复制代码
而后执行 ps -ef | grep redis
查看是否启动成功:
能够看到 6
个 Redis 节点都以集群的方式成功启动了,可是如今每一个节点还处于独立的状态,也就是说它们每个都各自成了一个集群,尚未互相联系起来,咱们须要手动地把他们之间创建起联系。
执行下列命令:
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
复制代码
--replicas 1
的意思是:咱们但愿为集群中的每一个主节点建立一个从节点。
观察控制台输出:
看到 [OK]
的信息以后,就表示集群已经搭建成功了,能够看到,这里咱们正确地建立了三主三从的集群。
咱们先使用 redic-cli
任意链接一个节点:
redis-cli -c -h 127.0.0.1 -p 7000
127.0.0.1:7000>
复制代码
-c
表示集群模式;
-h
指定 ip 地址;
-p
指定端口。
而后随便 set
一些值观察控制台输入:
127.0.0.1:7000> SET name wmyskxz
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001>
复制代码
能够看到这里 Redis 自动帮咱们进行了 Redirected
操做跳转到了 7001
这个实例上。
咱们再使用 cluster info
(查看集群信息) 和 cluster nodes
(查看节点列表) 来分别看看:(任意节点输入都可)
127.0.0.1:7001> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:1365
cluster_stats_messages_pong_sent:1358
cluster_stats_messages_meet_sent:4
cluster_stats_messages_sent:2727
cluster_stats_messages_ping_received:1357
cluster_stats_messages_pong_received:1369
cluster_stats_messages_meet_received:1
cluster_stats_messages_received:2727
127.0.0.1:7001> CLUSTER NODES
56a04742f36c6e84968cae871cd438935081e86f 127.0.0.1:7003@17003 slave 4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 0 1584428884000 4 connected
4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 127.0.0.1:7000@17000 master - 0 1584428884000 1 connected 0-5460
e2539c4398b8258d3f9ffa714bd778da107cb2cd 127.0.0.1:7005@17005 slave a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 0 1584428885222 6 connected
d31cd1f423ab1e1849cac01ae927e4b6950f55d9 127.0.0.1:7004@17004 slave 236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 0 1584428884209 5 connected
236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 127.0.0.1:7001@17001 myself,master - 0 1584428882000 2 connected 5461-10922
a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 127.0.0.1:7002@17002 master - 0 1584428884000 3 connected 10923-16383
127.0.0.1:7001>
复制代码
哈希取余分区思路很是简单:计算 key
的 hash 值,而后对节点数量进行取余,从而决定数据映射到哪一个节点上。
不过该方案最大的问题是,当新增或删减节点时,节点数量发生变化,系统中全部的数据都须要 从新计算映射关系,引起大规模数据迁移。
一致性哈希算法将 整个哈希值空间 组织成一个虚拟的圆环,范围是 [0 - 232 - 1],对于每个数据,根据 key
计算 hash 值,确数据在环上的位置,而后今后位置沿顺时针行走,找到的第一台服务器就是其应该映射到的服务器:
与哈希取余分区相比,一致性哈希分区将 增减节点的影响限制在相邻节点。以上图为例,若是在 node1
和 node2
之间增长 node5
,则只有 node2
中的一部分数据会迁移到 node5
;若是去掉 node2
,则原 node2
中的数据只会迁移到 node4
中,只有 node4
会受影响。
一致性哈希分区的主要问题在于,当 节点数量较少 时,增长或删减节点,对单个节点的影响可能很大,形成数据的严重不平衡。仍是以上图为例,若是去掉 node2
,node4
中的数据由总数据的 1/4
左右变为 1/2
左右,与其余节点相比负载太高。
该方案在 一致性哈希分区的基础上,引入了 虚拟节点 的概念。Redis 集群使用的即是该方案,其中的虚拟节点称为 槽(slot)。槽是介于数据和实际节点之间的虚拟概念,每一个实际节点包含必定数量的槽,每一个槽包含哈希值在必定范围内的数据。
在使用了槽的一致性哈希分区中,槽是数据管理和迁移的基本单位。槽 解耦 了 数据和实际节点 之间的关系,增长或删除节点对系统的影响很小。仍以上图为例,系统中有 4
个实际节点,假设为其分配 16
个槽(0-15);
若是此时删除 node2
,只须要将槽 4-7 从新分配便可,例如槽 4-5 分配给 node1
,槽 6 分配给 node3
,槽 7 分配给 node4
;能够看出删除 node2
后,数据在其余节点的分布仍然较为均衡。
集群的创建离不开节点之间的通讯,例如咱们上访在 快速体验 中刚启动六个集群节点以后经过 redis-cli
命令帮助咱们搭建起来了集群,实际上背后每一个集群之间的两两链接是经过了 CLUSTER MEET <ip> <port>
命令发送 MEET
消息完成的,下面咱们展开详细说说。
在 哨兵系统 中,节点分为 数据节点 和 哨兵节点:前者存储数据,后者实现额外的控制功能。在 集群 中,没有数据节点与非数据节点之分:全部的节点都存储数据,也都参与集群状态的维护。为此,集群中的每一个节点,都提供了两个 TCP 端口:
7000
节点的集群端口为
17000
。
集群端口只用于节点之间的通讯,如搭建集群、增减节点、故障转移等操做时节点间的通讯;不要使用客户端链接集群接口。为了保证集群能够正常工做,在配置防火墙时,要同时开启普通端口和集群端口。
节点间通讯,按照通讯协议能够分为几种类型:单对单、广播、Gossip 协议等。重点是广播和 Gossip 的对比。
集群中的节点采用 固定频率(每秒10次) 的 定时任务 进行通讯相关的工做:判断是否须要发送消息及消息类型、肯定接收节点、发送消息等。若是集群状态发生了变化,如增减节点、槽状态变动,经过节点间的通讯,全部节点会很快得知整个集群的状态,使集群收敛。
节点间发送的消息主要分为 5
种:meet 消息
、ping 消息
、pong 消息
、fail 消息
、publish 消息
。不一样的消息类型,通讯协议、发送的频率和时机、接收节点的选择等是不一样的:
CLUSTER MEET
命令时,会向新加入的节点发送
MEET
消息,请求新节点加入到当前集群;新节点收到 MEET 消息后会回复一个
PONG
消息。
PING
消息,接收者收到消息后会回复一个
PONG
消息。
PING 消息的内容是自身节点和部分其余节点的状态信息,做用是彼此交换信息,以及检测节点是否在线。
PING
消息使用 Gossip 协议发送,接收节点的选择兼顾了收敛速度和带宽成本,
具体规则以下:(1)随机找 5 个节点,在其中选择最久没有通讯的 1 个节点;(2)扫描节点列表,选择最近一次收到
PONG
消息时间大于
cluster_node_timeout / 2
的全部节点,防止这些节点长时间未更新。
PONG
消息封装了自身状态数据。能够分为两种:
第一种 是在接到
MEET/PING
消息后回复的
PONG
消息;
第二种 是指节点向集群广播
PONG
消息,这样其余节点能够获知该节点的最新信息,例如故障恢复后新的主节点会广播
PONG
消息。
FAIL
状态时,会向集群广播这一
FAIL
消息;接收节点会将这一
FAIL
消息保存起来,便于后续的判断。
PUBLISH
命令后,会先执行该命令,而后向集群广播这一消息,接收节点也会执行该
PUBLISH
命令。
节点须要专门的数据结构来存储集群的状态。所谓集群的状态,是一个比较大的概念,包括:集群是否处于上线状态、集群中有哪些节点、节点是否可达、节点的主从状态、槽的分布……
节点为了存储集群状态而提供的数据结构中,最关键的是 clusterNode
和 clusterState
结构:前者记录了一个节点的状态,后者记录了集群做为一个总体的状态。
clusterNode
结构保存了 一个节点的当前状态,包括建立时间、节点 id、ip 和端口号等。每一个节点都会用一个 clusterNode
结构记录本身的状态,并为集群内全部其余节点都建立一个 clusterNode
结构来记录节点状态。
下面列举了 clusterNode
的部分字段,并说明了字段的含义和做用:
typedef struct clusterNode {
//节点建立时间
mstime_t ctime;
//节点id
char name[REDIS_CLUSTER_NAMELEN];
//节点的ip和端口号
char ip[REDIS_IP_STR_LEN];
int port;
//节点标识:整型,每一个bit都表明了不一样状态,如节点的主从状态、是否在线、是否在握手等
int flags;
//配置纪元:故障转移时起做用,相似于哨兵的配置纪元
uint64_t configEpoch;
//槽在该节点中的分布:占用16384/8个字节,16384个比特;每一个比特对应一个槽:比特值为1,则该比特对应的槽在节点中;比特值为0,则该比特对应的槽不在节点中
unsigned char slots[16384/8];
//节点中槽的数量
int numslots;
…………
} clusterNode;
复制代码
除了上述字段,clusterNode
还包含节点链接、主从复制、故障发现和转移须要的信息等。
clusterState
结构保存了在当前节点视角下,集群所处的状态。主要字段包括:
typedef struct clusterState {
//自身节点
clusterNode *myself;
//配置纪元
uint64_t currentEpoch;
//集群状态:在线仍是下线
int state;
//集群中至少包含一个槽的节点数量
int size;
//哈希表,节点名称->clusterNode节点指针
dict *nodes;
//槽分布信息:数组的每一个元素都是一个指向clusterNode结构的指针;若是槽尚未分配给任何节点,则为NULL
clusterNode *slots[16384];
…………
} clusterState;
复制代码
除此以外,clusterState
还包括故障转移、槽迁移等须要的信息。
更多关于集群内容请自行阅读《Redis 设计与实现》,其中有更多细节方面的介绍 - redisbook.com/
本文已收录至个人 Github 程序员成长系列 【More Than Java】,学习,不止 Code,欢迎 star:github.com/wmyskxz/Mor… 我的公众号 :wmyskxz, 我的独立域名博客:wmyskxz.com,坚持原创输出,下方扫码关注,2020,与您共同成长!
很是感谢各位人才能 看到这里,若是以为本篇文章写得不错,以为 「我没有三颗心脏」有点东西 的话,求点赞,求关注,求分享,求留言!
创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见!
本文使用 mdnice 排版