建立集群

Cluster MEET IP host 命令用于将指定的ip,host所属的节点添加到当前的集群中。例如客户端连接7000节点,向7000节点发送 CLUSTER MEET 127.0.0.1 7001指令,则7000与7001握手之后,7001加入7000所在的集群中。
具体步骤:

  1. 收到CLUSTER MEET命令的7000会为7001创建一个clusterNode结构,并且将这个node添加到自己的clusterState.nodes字典里
    然后向7001发送一条MEET消息
  2. 7001如果顺利接收到MEET消息,也会为7000创建一个clusterNode节点然后添加到自己的字典里,随后7001回复一个PONG命令给7000
  3. 7000如果成功接收到7001命令,回复一个PING命令,握手完成
  4. 随后7000将7001的信息通过gossip协议传播给集群中的其他节点

clusterNode结构保存了一个节点的当前状态,例如节点地址,节点名字,创建时间等等。每一个节点还保存着一个clusterState结构,这个结构里保存着当前节点视角下集群的其他节点所处的状态。

gossip协议:是一种周期性散播消息的协议,种子节点随机选择几个节点散播消息,接受到消息的节点也会做同样的操作,直到所有的节点都收到了消息,就好像一个八卦在办公室中悄悄传播,最后所有人都知道了的过程,因此称之为gossip协议。gossip协议具有扩展性好,容错率高,去中心化,简单等优点。

槽指派

整个集群有16384个槽用来存放数据,如果所有的槽都被指派给了某个节点管理,那么这个集群就处于上线的状态,否则就处于离线的状态。

  • clusterNode的slots属性和numsslot属性记录了节点负责处理哪些槽
  • slots属性是一个位数组,长度为16384/8,共2048个字节,包含16384个二进制位,负责的槽对应的数组位置值为1,否则为0
  • 一个节点除了知道自己负责的槽,还会将自己负责的槽信息传播给其他节点。节点收到其他节点的槽信息时,会在自己的clusterState.nodes字典里找到这个节点对应的clusterNode,然后将信息更新到它的slots属性中。也就是说,集群中的任意一个节点,都保存着这个集群所有的槽指派信息。
  • CLUSTER KEYSLOT 可以查看一个键属于哪个槽
    节点计算出当前key所属的槽后,会检查clusterState.slots数组中槽的对应项,而不是查看自己的slots数组,这么做是因为查看 了自己的数组以后如果不是自己负责,那还是得再检查一边clusterState.slots来提示客户端。
  • 如果当前指令所属的槽不由自己负责,节点会返回个客户端MOVED错误,并指引客户端指向正确的节点。MOVED命令的格式为:MOVED :
  • 一个集群客户端通常会与集群的多个节点创立套接字连接,所以客户端接收到MOVED错误以后会转向另一个套接字发送。
  • 虽然REDIS有多个数据库可以选择,但是在集群模式中只能使用0号数据库。

复制

集群中master node和slave node需要复制数据。除非你很任性,Redis Cluster里全是master node。
关于复制 可以直达《Redis学习笔记之主从复制》

哨兵

redis集群中各个节点之间的存活情况是由哨兵节点来监听的。

什么是哨兵Sentinel

Sentinel本质是运行在特殊模式下的redis服务器。它启动的步骤和启动一个redis普通服务器是一样的,包括创建struct redisServer的实例变量,载入配置文件redis.conf等,但是哨兵并不需要进行数据库操作,所以不进行读RDB或AOF文件数据载入等操作。不涉及到数据操作,那么正常的set,get等命令,以及事务命令,脚本命令,持久化命令都不支持,但是复制命令,发布订阅等功能可以在哨兵节点内部使用。
在完成一个redis服务节点的初始化之后,哨兵节点还会做一些自己的专属操作。

  1. 载入专属的代码命令表。例如哨兵节点的默认端口serverport和普通redis服务节点使用的值不同。
  2. 初始化一个类型为sentinelState的结构,保存了所有和哨兵功能有关的服务器状态。
  3. 初始化监视的服务器节点的信息。sentinelState的master字典中保存了所有要监视的节点的信息,例如实例的ip和端口号,无响应多久判断为下线,等等。
  4. 同监视的节点创立网络连接。创立连接后,哨兵节点成为被监视的服务节点的客户端。一般来说, 哨兵节点向服务节点创建两个连接,一个是命令连接,用于发送命令的通信,另外一个是订阅连接,用来订阅服务器的sentinel:hello频道。这么做可以保证多个哨兵都订阅了同一个节点的同一个频道,那么其中一个哨兵断线或不可用不会影响集群对服务节点可用性的判断。

哨兵与服务器的连接

sentinel每隔10秒向主服务器节点发送INFO命令,来获取主服务器的信息,以及它下属的从节点的信息。这样一来,有新上线的从服务器节点,sentinel就可以及时发现,并且与其建立连接。(多说一句,由于这样的心跳会有10s左右的延迟,那么是不是在这10s内如果主节点挂了,新上线的从节点就发现不了了,但是如果有多个哨兵可以降低这种情况发生的机率)
在检测到从服务器节点后,sentinel也会每隔10秒向从服务器发送INFO命令,从从服务器返回的结果中提取出其从属的master节点,角色role等信息。
sentinel还会每隔2秒向连接的所有服务器发送PUBLISH命令,包括了sentinel节点自己的信息,以及对应节点的主节点的信息,如果对应节点就是主节点那么信息就是对应节点自己的信息。
除了主动的发送命令之外,sentinel还会接收到来自监视的服务节点的订阅信息。订阅的信息可以提取出监听的节点还被哪些其他的sentinel同时监听,并且及时更新到该节点的实例对象的sentinels字典中。
默认情况下,sentinel以每秒1次的频率向已经建立连接的服务器节点发送PING命令,在配置的down-after-milliseconds毫秒内,实例无响应,则认为实例主观下线。认定为主观下线后,sentinel会向其他的sentinel节点也询问这个实例的状态,如果接收到足够数量的主观下线判断之后,settinel会认定该实例已经客观下线,若该节点是主节点,则开始进行故障转移操作。

故障转移

sentinel在确认主节点已经下线之后,在该主节点下的所有从节点中挑选出一个作为新的主节点。选点的依据依次是:网络连接正常->5秒内回复过INFO命令->10down-after-milliseconds内与主连接过的->从服务器优先级->复制偏移量->运行id较小的。选出之后通过slaveof no one命令将该从服务器升为新主服务器
当主服务器宕机之后,从服务器切换成主服务器的时间内,服务是不可用的
新的主节点选举产生之后,若原先的主节点重新上线,会被当成新的从节点对待,sentinel向其发送*SLAVEOF
命令。

搬运自我的简书 https://www.jianshu.com/p/8b59662229e6