redis源码分析(八):集群--cluster

作者: msrpp | 来源:发表于2018-09-11 22:03 被阅读95次

    redis源码分析(八):集群--cluster

    redis集群我们可以使用sentinel的模式(详情点击这里),这个模式有几个缺点

    • 1.sentinel是用来监控redis的,这个进程本该对客户端隐藏,但是sentinel模式下,master如果down了,某个slave成为master后,客户端无法感知,因此需要客户端还需要连接sentinel来获取master的地址。
    • 2.sentinel还是一主多从的模式,master的压力比较大。

    redis官方还提供了另一种集群的方式 - cluster, 一个集群中数据根据其key的哈希值被分别存储到 0 ~ 16383个槽中(slot)。每个槽均要被唯一添加到集群master节点中,在所有槽都被添加以后,集群的状态才会变成可用。

    集群搭建

    如果需要启用cluster集群的话,需要更改配置如下:

    port 7001
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 15000
    appendonly yes
    
    

    拷贝6份可执行程序到不同文件夹,从node1到node6,端口范围7001~7006,编写脚本startup.sh以便调试,内容如下:

    pkill redis-server
    
    cd node1
    ./redis-server ./redis.conf &
    cd ..
    
    cd node2
    ./redis-server ./redis.conf &
    cd ..
    
    cd node3
    ./redis-server ./redis.conf &
    cd ..
    
    cd node4
    ./redis-server ./redis.conf &
    cd ..
    
    cd node5
    ./redis-server ./redis.conf &
    cd ..
    
    cd node6
    ./redis-server ./redis.conf &
    cd ..
    

    编写cleanup.sh来清除节点信息和数据

    rm ./*/nodes.conf
    rm ./*/*.aof
    

    启动一个干净的集群集合以后。

    我们通过一下操作来创建一个简单的集群。1.连接各节点,2.分配槽 3.用cluster nodes 获取到各节点的名称,然后用cluster replicate设置各个slave的master节点。

    
    1. redis-cli -p 7001 
    
    > cluster meet 127.0.0.1 7002
    
    > cluster meet 127.0.0.1 7003
    
    > cluster meet 127.0.0.1 7004
    
    > cluster meet 127.0.0.1 7005
    
    > cluster meet 127.0.0.1 7006
    
    
    
    2. redis-cli -h 127.0.0.1 -p 7001 cluster addslots {0..5461}
    
       redis-cli -h 127.0.0.1 -p 7002 cluster addslots {5462..10922}
    
       redis-cli -h 127.0.0.1 -p 7003 cluster addslots {10923..16383}
    
    
    3. redis-cli -p 7004 //重复三次
    > cluster replicate $MasterName
    
    
    

    用redis-cli -c 来连接集群服务端,每次写操作,如果key映射出来的槽不是这个节点处理的,他会返回一条MOVED错误和应该接收这条写命令的master节点信息。客户端需要重定向到这个地址去写入数据。

    流程分析

    接下来我们就可以将代码附加到其中一个程序开始观察整个流程了。

    redis启动时除了监听port端口以外,还启动了port+10000的端口,用于集群服务各进程间互联,每个客户端连接的socket处理读事件的函数是clusterReadHandler。

    集群的命令

    集群模式下有个重要的命令"cluster",下面解析几个重要的:

    • cluster meet $host $port 与其他集群的节点建立链接。

    • cluster info 查看当前集群的简略信息。

    • cluster nodes 查看当前的节点信息。

    • cluster addslots/delslots $slot1 $slot2 ... $slotn 添加(删除)槽到当前节点。可以使用 redis-cli -h $host -p $port cluster addslots {$begin..$end} 来批量添加的连续end-begin个槽,(进入了客户端内后敲不行)。注意同一个槽不能重复添加。

    • cluster flushslots 删除当前所有的槽。

    • cluster setslot $slot migrating $targetName 将$slot槽从本节点迁移到目标节点,节点名称为每个节点的runid。

    • cluster setslot $slot importing $sourceName 将$slot槽从源节点迁移到本节点,为上条的逆操作。

    • cluster keyslot $key 计算$key应该被放到哪个槽中。

    • cluster replicate $targetName 设置当前节点为slave,并从对应的master中复制数据,注意slave有旧数据或被分配了槽会操作失败。

    这里有一个小插曲,(测试的时候发现还没有调用addslots来添加槽,有的节点就凭白无故多了几个固定的槽,后来跟了代码发现是该节点aof文件没有清除,导致了节点启动的时候读取数据并加给自己了槽。)

    集群的时间事件。

    集群的时间事件处理函数是clusterCron。我们看看它都干了什么。

    1.连接集群内的其他未连接上的服务进程,调用"cluster meet $ip $port"时,会新建一个集群节点放入“server.cluster->nodes”,在下次时间周期到来时连接port+10000端口,并设置读事件处理函数为clusterReadHandler,注意到这个和本地监听的集群端口处理函数是同一个。首次连接,给其发送meet命令(断线重连,直接发ping)。

    2.每十次事件循环执行一次,随机ping五个集群内的其他进程。

    3.断开连接超时的链接,在下个周期会重连。

    4.由slave来统计各个master上的slave数量。如果当前有master下的slave都挂了,且当前的集群保存最多slave的master上有大于2个的slave,那么可以分一个slave给那个"光杆司令"master。具体的迁移处理函数是clusterHandleSlaveMigration,具体算法就是对比各个slave的runid字符串,如果自身的最小,则开始迁移

    5.如果主节点挂了(超半数节点认为他挂了),而且当前节点是它的slave,那么此时开始发送一个故障转移授权请求。

    6.故障转移,如果当前节点是slave,对应的master挂了且获得了故障转移权限,则开始故障转移,将自身升级为master,继承原master的槽,并通知其他节点自己升级了。此时如果原master又启动了,发现纪元比新的master小,只能降级为slave。

    对集群中其他节点发来消息的处理

    处理函数是上文提及的clusterReadHandler,我们直接看调用到的核心函数clusterProcessPacket。

    1.处理ping和meet消息,均返回pong,将新消息的数据更新到本节点。注意到在所有ping,pong,meet消息中,均会去尝试携带二者(自己和对方)之外节点的gossip信息,特别是状态信息。这个机制保证了,在有限的时间内,所有节点均可以meet each other。

    2.如果有从节点的master发生了变更,修改本地数据。如果有master的槽信息发生了变化,更新之。

    3.异常处理--(如果有master重启可能会发生。)如果对方是master,且本节点记录的master中,管理的槽和对方有重复。则通知纪元较小的修正;同理,接收到了CLUSTERMSG_TYPE_UPDATE请求,如果本节点的纪元较小,也需更新本节点的槽信息。

    4.解析对方节点A的gossip信息,如果gossip中的目标节点B的状态是疑似下线或者已下线,将A添加节点B的failedlist中。如果大多数节点都认为这个节点下线了,那么我们将其从疑似下线转变为下线。

    5.假设master A下线了,其slave会发起一个故障转移的授权请求,只有master有投票权。所有接受到该消息的master节点如果确定A确实下线了,那么投B一票。投票的过程和sentinel选举新的master的过程类似,都是基于纪元累增,胜出的slave将执行故障转移。

    只是略读了一下代码,大致理清楚了其中的业务处理和故障处理流程,master挂了各节点会怎么做? slave挂了各节点会怎么做?

    但是集群涉及到多个节点共同合作,细节很多,比如数据一致性的问题。自己脑袋确实不够用了,先写到这里吧。

    相关文章

      网友评论

        本文标题:redis源码分析(八):集群--cluster

        本文链接:https://www.haomeiwen.com/subject/envjgftx.html