????Redis集群中的節(jié)點(diǎn)分為主節(jié)點(diǎn)(master)和從節(jié)點(diǎn)(slave),其中主節(jié)點(diǎn)用于處理槽,而從節(jié)點(diǎn)則用于復(fù)制某個(gè)主節(jié)點(diǎn),并在被復(fù)制的主節(jié)點(diǎn)下線時(shí),代替下線主節(jié)點(diǎn)繼續(xù)處理命令請(qǐng)求。
設(shè)置從節(jié)點(diǎn)
CLUSTER REPLICATE <node_id>
#讓接收命令的節(jié)點(diǎn)成為node_id所指定節(jié)點(diǎn)的從節(jié)點(diǎn),并開(kāi)始對(duì)主節(jié)點(diǎn)進(jìn)行主從復(fù)制。
過(guò)程:
1.接收到該命令的節(jié)點(diǎn)首先會(huì)在自己的clusterState.nodes字典中找到node_id 所對(duì)應(yīng)節(jié)點(diǎn)的clusterNode結(jié)構(gòu),并將自己的clusterState.myself.slaveof 指針指向這個(gè)結(jié)構(gòu),以此來(lái)記錄這個(gè)節(jié)點(diǎn)正在復(fù)制的主節(jié)點(diǎn)∶
struct clusterNode {
// 如果這是一個(gè)從節(jié)點(diǎn),那么指向主節(jié)點(diǎn)
struct clusterNode *slaveof;
}
2.然后節(jié)點(diǎn)會(huì)修改自己在clusterState.myself.flags中的屬性,關(guān)閉原本的REDIS_NODE_MASTER標(biāo)識(shí),打開(kāi)REDIS_NODE_SLAVE標(biāo)識(shí),表示這個(gè)節(jié)點(diǎn)已經(jīng)由原來(lái)的主節(jié)點(diǎn)變成了從節(jié)點(diǎn)。
3.最后,節(jié)點(diǎn)會(huì)調(diào)用復(fù)制代碼,并根據(jù)clusterState.myself.slaveof指向的clusterNode結(jié)構(gòu)所保存的IP地址和端口號(hào),對(duì)主節(jié)點(diǎn)進(jìn)行復(fù)制。因?yàn)楣?jié)點(diǎn)的復(fù)制功能和單機(jī) Redis服務(wù)器的復(fù)制功能使用了相同的代碼,所以讓從節(jié)點(diǎn)復(fù)制主節(jié)點(diǎn)相當(dāng)于向從節(jié)點(diǎn)發(fā)送命令SLAVEOF<master_ip><master_port>。
下圖表示從節(jié)點(diǎn)7004正在復(fù)制主節(jié)點(diǎn)7000
image.png
故障檢測(cè)
????集群中的每個(gè)節(jié)點(diǎn)都會(huì)定期向其他節(jié)點(diǎn)發(fā)送PING消息,用來(lái)檢測(cè)對(duì)方是否在線,如果接收PING消息的節(jié)點(diǎn)沒(méi)有在規(guī)定的時(shí)間內(nèi),向發(fā)送PING 消息的節(jié)點(diǎn)返回PONG消息,那么發(fā)送PING消息的節(jié)點(diǎn)就會(huì)將接收PING消息的節(jié)點(diǎn)標(biāo)記為疑似下線
????集群中的各個(gè)節(jié)點(diǎn)會(huì)通過(guò)互相發(fā)送消息的方式來(lái)交換集群中各個(gè)節(jié)點(diǎn)的狀態(tài)信息,例如某個(gè)節(jié)點(diǎn)是處于在線狀態(tài)、疑似下線狀態(tài)(PFAIL),還是已下線狀態(tài)(FAIL)。
????當(dāng)一個(gè)主節(jié)點(diǎn)A通過(guò)消息得知主節(jié)點(diǎn)B認(rèn)為主節(jié)點(diǎn)C進(jìn)入了疑似下線狀態(tài)時(shí),主節(jié)點(diǎn)A會(huì)在自己的clusterState.nodes字典中找到主節(jié)點(diǎn)C所對(duì)應(yīng)的clusterNode結(jié)構(gòu),并將主節(jié)點(diǎn)B的下線報(bào)告(failure report)添加到clusterNode結(jié)構(gòu)的fail_reports鏈表中。
struct clusterNode {
// 一個(gè)鏈表,記錄了所有其他節(jié)點(diǎn)對(duì)該節(jié)點(diǎn)的下線報(bào)告
list *fail_reports;
}
????比如,如果主節(jié)點(diǎn)7001在收到主節(jié)點(diǎn)7002、主節(jié)點(diǎn)7003發(fā)送的消息后得知,主節(jié)點(diǎn)7002和主節(jié)點(diǎn)7003都認(rèn)為主節(jié)點(diǎn)7000進(jìn)入了疑似下線狀態(tài),那么主節(jié)點(diǎn)7001將為主節(jié)點(diǎn)7000創(chuàng)建下圖所示的下線報(bào)告。

????如果在一個(gè)集群里面,半數(shù)以上負(fù)責(zé)處理槽的主節(jié)點(diǎn)都將某個(gè)主節(jié)點(diǎn)x報(bào)告為疑似下線,那么這個(gè)主節(jié)點(diǎn)x將被標(biāo)記為已下線(FAIL),將主節(jié)點(diǎn)x標(biāo)記為已下線的節(jié)點(diǎn)會(huì)向集群廣播一條關(guān)于主節(jié)點(diǎn)x的FAIL消息,所有收到這條 FAIL消息的節(jié)點(diǎn)都會(huì)立即將主節(jié)點(diǎn)x標(biāo)記為已下線。
故障轉(zhuǎn)移
????當(dāng)一個(gè)從節(jié)點(diǎn)發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點(diǎn)進(jìn)入了已下線狀態(tài)時(shí),從節(jié)點(diǎn)將開(kāi)始對(duì)下線主節(jié)點(diǎn)進(jìn)行故障轉(zhuǎn)移,以下是故障轉(zhuǎn)移的執(zhí)行步驟∶
1)復(fù)制下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)里面,會(huì)有一個(gè)從節(jié)點(diǎn)被選中。
2)被選中的從節(jié)點(diǎn)會(huì)執(zhí)行 SLAVE OF no one命令,成為新的主節(jié)點(diǎn)。
3)新的主節(jié)點(diǎn)會(huì)撤銷(xiāo)所有對(duì)已下線主節(jié)點(diǎn)的槽指派,并將這些槽全部指派給自己。
4)新的主節(jié)點(diǎn)向集群廣播一條PONG消息,這條 PONG消息可以讓集群中的其他節(jié)點(diǎn)立即知道這個(gè)節(jié)點(diǎn)已經(jīng)由從節(jié)點(diǎn)變成了主節(jié)點(diǎn),并且這個(gè)主節(jié)點(diǎn)已經(jīng)接管了原本由已下線節(jié)點(diǎn)負(fù)責(zé)處理的槽。
5)新的主節(jié)點(diǎn)開(kāi)始接收和自己負(fù)責(zé)處理的槽有關(guān)的命令請(qǐng)求,故障轉(zhuǎn)移完成。
選舉新的主節(jié)點(diǎn)
1)集群的配置紀(jì)元是一個(gè)自增計(jì)數(shù)器,它的初始值為0。
2)當(dāng)集群里的某個(gè)節(jié)點(diǎn)開(kāi)始一次故障轉(zhuǎn)移操作時(shí),集群配置紀(jì)元的值會(huì)被增一。
3)對(duì)于每個(gè)配置紀(jì)元,集群里每個(gè)負(fù)責(zé)處理槽的主節(jié)點(diǎn)都有一次投票的機(jī)會(huì),而第一個(gè)向主節(jié)點(diǎn)要求投票的從節(jié)點(diǎn)將獲得主節(jié)點(diǎn)的投票。
4)當(dāng)從節(jié)點(diǎn)發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點(diǎn)進(jìn)入已下線狀態(tài)時(shí),從節(jié)點(diǎn)會(huì)向集群廣播一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息、并且具有投票權(quán)的主節(jié)點(diǎn)向這個(gè)從節(jié)點(diǎn)投票。
5)如果一個(gè)主節(jié)點(diǎn)具有投票權(quán)(它正在負(fù)責(zé)處理槽),并且這個(gè)主節(jié)點(diǎn)尚未投票給其他從節(jié)點(diǎn),那么主節(jié)點(diǎn)將向要求投票的從節(jié)點(diǎn)返回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示這個(gè)主節(jié)點(diǎn)支持從節(jié)點(diǎn)成為新的主節(jié)點(diǎn)。
6)每個(gè)參與選舉的從節(jié)點(diǎn)都會(huì)接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根據(jù)自己收到了多少條這種消息來(lái)統(tǒng)計(jì)自己獲得了多少主節(jié)點(diǎn)的支持。
7)如果集群里有N個(gè)具有投票權(quán)的主節(jié)點(diǎn),那么當(dāng)一個(gè)從節(jié)點(diǎn)收集到大于等于N/2+1 張支持票時(shí),這個(gè)從節(jié)點(diǎn)就會(huì)當(dāng)選為新的主節(jié)點(diǎn)。
8)因?yàn)樵诿恳粋€(gè)配置紀(jì)元里面,每個(gè)具有投票權(quán)的主節(jié)點(diǎn)只能投一次票,所以如果有N個(gè)主節(jié)點(diǎn)進(jìn)行投票,那么具有大于等于N/2+1 張支持票的從節(jié)點(diǎn)只會(huì)有一個(gè),這確保了新的主節(jié)點(diǎn)只會(huì)有一個(gè)。
9)如果在一個(gè)配置紀(jì)元里面沒(méi)有從節(jié)點(diǎn)能收集到足夠多的支持票,那么集群進(jìn)入一個(gè)新的配置紀(jì)元,并再次進(jìn)行選舉,直到選出新的主節(jié)點(diǎn)為止。
