婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁(yè) > 知識(shí)庫(kù) > Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解

Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解

熱門(mén)標(biāo)簽:小紅書(shū)怎么地圖標(biāo)注店 地圖標(biāo)注如何即時(shí)生效 百度商家地圖標(biāo)注怎么做 西藏教育智能外呼系統(tǒng)價(jià)格 竹間科技AI電銷(xiāo)機(jī)器人 地圖標(biāo)注費(fèi)用 最簡(jiǎn)單的百度地圖標(biāo)注 太原營(yíng)銷(xiāo)外呼系統(tǒng) 玄武湖地圖標(biāo)注

一:手動(dòng)故障轉(zhuǎn)移

         Redis集群支持手動(dòng)故障轉(zhuǎn)移。也就是向從節(jié)點(diǎn)發(fā)送”CLUSTER  FAILOVER”命令,使其在主節(jié)點(diǎn)未下線的情況下,發(fā)起故障轉(zhuǎn)移流程,升級(jí)為新的主節(jié)點(diǎn),而原來(lái)的主節(jié)點(diǎn)降級(jí)為從節(jié)點(diǎn)。

         為了不丟失數(shù)據(jù),向從節(jié)點(diǎn)發(fā)送”CLUSTER  FAILOVER”命令后,流程如下:

         a:從節(jié)點(diǎn)收到命令后,向主節(jié)點(diǎn)發(fā)送CLUSTERMSG_TYPE_MFSTART包;
         b:主節(jié)點(diǎn)收到該包后,會(huì)將其所有客戶端置于阻塞狀態(tài),也就是在10s的時(shí)間內(nèi),不再處理客戶端發(fā)來(lái)的命令;并且在其發(fā)送的心跳包中,會(huì)帶有CLUSTERMSG_FLAG0_PAUSED標(biāo)記;
         c:從節(jié)點(diǎn)收到主節(jié)點(diǎn)發(fā)來(lái)的,帶CLUSTERMSG_FLAG0_PAUSED標(biāo)記的心跳包后,從中獲取主節(jié)點(diǎn)當(dāng)前的復(fù)制偏移量。從節(jié)點(diǎn)等到自己的復(fù)制偏移量達(dá)到該值后,才會(huì)開(kāi)始執(zhí)行故障轉(zhuǎn)移流程:發(fā)起選舉、統(tǒng)計(jì)選票、贏得選舉、升級(jí)為主節(jié)點(diǎn)并更新配置;

         ”CLUSTER  FAILOVER”命令支持兩個(gè)選項(xiàng):FORCE和TAKEOVER。使用這兩個(gè)選項(xiàng),可以改變上述的流程。

         如果有FORCE選項(xiàng),則從節(jié)點(diǎn)不會(huì)與主節(jié)點(diǎn)進(jìn)行交互,主節(jié)點(diǎn)也不會(huì)阻塞其客戶端,而是從節(jié)點(diǎn)立即開(kāi)始故障轉(zhuǎn)移流程:發(fā)起選舉、統(tǒng)計(jì)選票、贏得選舉、升級(jí)為主節(jié)點(diǎn)并更新配置。

         如果有TAKEOVER選項(xiàng),則更加簡(jiǎn)單粗暴:從節(jié)點(diǎn)不再發(fā)起選舉,而是直接將自己升級(jí)為主節(jié)點(diǎn),接手原主節(jié)點(diǎn)的槽位,增加自己的configEpoch后更新配置。

         因此,使用FORCE和TAKEOVER選項(xiàng),主節(jié)點(diǎn)可以已經(jīng)下線;而不使用任何選項(xiàng),只發(fā)送”CLUSTER  FAILOVER”命令的話,主節(jié)點(diǎn)必須在線。

        在clusterCommand函數(shù)中,處理”CLUSTER  FAILOVER”命令的部分代碼如下:

else if (!strcasecmp(c->argv[1]->ptr,"failover")  
      (c->argc == 2 || c->argc == 3)) 
{ 
  /* CLUSTER FAILOVER [FORCE|TAKEOVER] */ 
  int force = 0, takeover = 0; 
 
  if (c->argc == 3) { 
    if (!strcasecmp(c->argv[2]->ptr,"force")) { 
      force = 1; 
    } else if (!strcasecmp(c->argv[2]->ptr,"takeover")) { 
      takeover = 1; 
      force = 1; /* Takeover also implies force. */ 
    } else { 
      addReply(c,shared.syntaxerr); 
      return; 
    } 
  } 
 
  /* Check preconditions. */ 
  if (nodeIsMaster(myself)) { 
    addReplyError(c,"You should send CLUSTER FAILOVER to a slave"); 
    return; 
  } else if (myself->slaveof == NULL) { 
    addReplyError(c,"I'm a slave but my master is unknown to me"); 
    return; 
  } else if (!force  
        (nodeFailed(myself->slaveof) || 
        myself->slaveof->link == NULL)) 
  { 
    addReplyError(c,"Master is down or failed, " 
            "please use CLUSTER FAILOVER FORCE"); 
    return; 
  } 
  resetManualFailover(); 
  server.cluster->mf_end = mstime() + REDIS_CLUSTER_MF_TIMEOUT; 
 
  if (takeover) { 
    /* A takeover does not perform any initial check. It just 
     * generates a new configuration epoch for this node without 
     * consensus, claims the master's slots, and broadcast the new 
     * configuration. */ 
    redisLog(REDIS_WARNING,"Taking over the master (user request)."); 
    clusterBumpConfigEpochWithoutConsensus(); 
    clusterFailoverReplaceYourMaster(); 
  } else if (force) { 
    /* If this is a forced failover, we don't need to talk with our 
     * master to agree about the offset. We just failover taking over 
     * it without coordination. */ 
    redisLog(REDIS_WARNING,"Forced failover user request accepted."); 
    server.cluster->mf_can_start = 1; 
  } else { 
    redisLog(REDIS_WARNING,"Manual failover user request accepted."); 
    clusterSendMFStart(myself->slaveof); 
  } 
  addReply(c,shared.ok); 
} 

首先檢查命令的最后一個(gè)參數(shù)是否是FORCETAKEOVER

         如果當(dāng)前節(jié)點(diǎn)是主節(jié)點(diǎn);或者當(dāng)前節(jié)點(diǎn)是從節(jié)點(diǎn),但沒(méi)有主節(jié)點(diǎn);或者當(dāng)前從節(jié)點(diǎn)的主節(jié)點(diǎn)已經(jīng)下線或者斷鏈,并且命令中沒(méi)有FORCE或TAKEOVER參數(shù),則直接回復(fù)客戶端錯(cuò)誤信息后返回;

         然后調(diào)用resetManualFailover,重置手動(dòng)強(qiáng)制故障轉(zhuǎn)移的狀態(tài);

         置mf_end為當(dāng)前時(shí)間加5秒,該屬性表示手動(dòng)強(qiáng)制故障轉(zhuǎn)移流程的超時(shí)時(shí)間,也用來(lái)表示當(dāng)前是否正在進(jìn)行手動(dòng)強(qiáng)制故障轉(zhuǎn)移;

         如果命令最后一個(gè)參數(shù)為T(mén)AKEOVER,這表示收到命令的從節(jié)點(diǎn)無(wú)需經(jīng)過(guò)選舉的過(guò)程,直接接手其主節(jié)點(diǎn)的槽位,并成為新的主節(jié)點(diǎn)。因此首先調(diào)用函數(shù)clusterBumpConfigEpochWithoutConsensus,產(chǎn)生新的configEpoch,以便后續(xù)更新配置;然后調(diào)用clusterFailoverReplaceYourMaster函數(shù),轉(zhuǎn)變成為新的主節(jié)點(diǎn),并將這種轉(zhuǎn)變廣播給集群中所有節(jié)點(diǎn);

         如果命令最后一個(gè)參數(shù)是FORCE,這表示收到命令的從節(jié)點(diǎn)可以直接開(kāi)始選舉過(guò)程,而無(wú)需達(dá)到主節(jié)點(diǎn)的復(fù)制偏移量之后才開(kāi)始選舉過(guò)程。因此置mf_can_start為1,這樣在函數(shù)clusterHandleSlaveFailover中,即使在主節(jié)點(diǎn)未下線或者當(dāng)前從節(jié)點(diǎn)的復(fù)制數(shù)據(jù)比較舊的情況下,也可以開(kāi)始故障轉(zhuǎn)移流程;

         如果最后一個(gè)參數(shù)不是FORCE或TAKEOVER,這表示收到命令的從節(jié)點(diǎn),首先需要向主節(jié)點(diǎn)發(fā)送CLUSTERMSG_TYPE_MFSTART包,因此調(diào)用clusterSendMFStart函數(shù),向其主節(jié)點(diǎn)發(fā)送該包;

         主節(jié)點(diǎn)收到CLUSTERMSG_TYPE_MFSTART包后,在clusterProcessPacket函數(shù)中,是這樣處理的:

else if (type == CLUSTERMSG_TYPE_MFSTART) { 
  /* This message is acceptable only if I'm a master and the sender 
   * is one of my slaves. */ 
  if (!sender || sender->slaveof != myself) return 1; 
  /* Manual failover requested from slaves. Initialize the state 
   * accordingly. */ 
  resetManualFailover(); 
  server.cluster->mf_end = mstime() + REDIS_CLUSTER_MF_TIMEOUT; 
  server.cluster->mf_slave = sender; 
  pauseClients(mstime()+(REDIS_CLUSTER_MF_TIMEOUT*2)); 
  redisLog(REDIS_WARNING,"Manual failover requested by slave %.40s.", 
    sender->name); 
} 

  如果字典中找不到發(fā)送節(jié)點(diǎn),或者發(fā)送節(jié)點(diǎn)的主節(jié)點(diǎn)不是當(dāng)前節(jié)點(diǎn),則直接返回;

         調(diào)用resetManualFailover,重置手動(dòng)強(qiáng)制故障轉(zhuǎn)移的狀態(tài);

         然后置mf_end為當(dāng)前時(shí)間加5秒,該屬性表示手動(dòng)強(qiáng)制故障轉(zhuǎn)移流程的超時(shí)時(shí)間,也用來(lái)表示當(dāng)前是否正在進(jìn)行手動(dòng)強(qiáng)制故障轉(zhuǎn)移;

         然后設(shè)置mf_slave為sender,該屬性表示要進(jìn)行手動(dòng)強(qiáng)制故障轉(zhuǎn)移的從節(jié)點(diǎn);

         然后調(diào)用pauseClients,使所有客戶端在之后的10s內(nèi)阻塞;

         主節(jié)點(diǎn)在發(fā)送心跳包時(shí),在構(gòu)建包頭時(shí),如果發(fā)現(xiàn)當(dāng)前正處于手動(dòng)強(qiáng)制故障轉(zhuǎn)移階段,則會(huì)在包頭中增加CLUSTERMSG_FLAG0_PAUSED標(biāo)記:

void clusterBuildMessageHdr(clusterMsg *hdr, int type) { 
  ... 
  /* Set the message flags. */ 
  if (nodeIsMaster(myself)  server.cluster->mf_end) 
    hdr->mflags[0] |= CLUSTERMSG_FLAG0_PAUSED; 
  ... 
}   

  從節(jié)點(diǎn)在clusterProcessPacket函數(shù)中處理收到的包,一旦發(fā)現(xiàn)主節(jié)點(diǎn)發(fā)來(lái)的,帶有CLUSTERMSG_FLAG0_PAUSED標(biāo)記的包,就會(huì)將該主節(jié)點(diǎn)的復(fù)制偏移量記錄到server.cluster->mf_master_offset中:

int clusterProcessPacket(clusterLink *link) { 
  ... 
  /* Check if the sender is a known node. */ 
  sender = clusterLookupNode(hdr->sender); 
  if (sender  !nodeInHandshake(sender)) { 
    ... 
    /* Update the replication offset info for this node. */ 
    sender->repl_offset = ntohu64(hdr->offset); 
    sender->repl_offset_time = mstime(); 
    /* If we are a slave performing a manual failover and our master 
     * sent its offset while already paused, populate the MF state. */ 
    if (server.cluster->mf_end  
      nodeIsSlave(myself)  
      myself->slaveof == sender  
      hdr->mflags[0]  CLUSTERMSG_FLAG0_PAUSED  
      server.cluster->mf_master_offset == 0) 
    { 
      server.cluster->mf_master_offset = sender->repl_offset; 
      redisLog(REDIS_WARNING, 
        "Received replication offset for paused " 
        "master manual failover: %lld", 
        server.cluster->mf_master_offset); 
    } 
  } 
}   

         從節(jié)點(diǎn)在集群定時(shí)器函數(shù)clusterCron中,會(huì)調(diào)用clusterHandleManualFailover函數(shù),判斷一旦當(dāng)前從節(jié)點(diǎn)的復(fù)制偏移量達(dá)到了server.cluster->mf_master_offset,就會(huì)置server.cluster->mf_can_start為1。這樣在接下來(lái)要調(diào)用的clusterHandleSlaveFailover函數(shù)中,就會(huì)立即開(kāi)始故障轉(zhuǎn)移流程了。

         clusterHandleManualFailover函數(shù)的代碼如下:

void clusterHandleManualFailover(void) { 
  /* Return ASAP if no manual failover is in progress. */ 
  if (server.cluster->mf_end == 0) return; 
  /* If mf_can_start is non-zero, the failover was already triggered so the 
   * next steps are performed by clusterHandleSlaveFailover(). */ 
  if (server.cluster->mf_can_start) return; 
  if (server.cluster->mf_master_offset == 0) return; /* Wait for offset... */ 
  if (server.cluster->mf_master_offset == replicationGetSlaveOffset()) { 
    /* Our replication offset matches the master replication offset 
     * announced after clients were paused. We can start the failover. */ 
    server.cluster->mf_can_start = 1; 
    redisLog(REDIS_WARNING, 
      "All master replication stream processed, " 
      "manual failover can start."); 
  } 
} 

  不管是從節(jié)點(diǎn),還是主節(jié)點(diǎn),在集群定時(shí)器函數(shù)clusterCron中,都會(huì)調(diào)用manualFailoverCheckTimeout函數(shù),一旦發(fā)現(xiàn)手動(dòng)故障轉(zhuǎn)移的超時(shí)時(shí)間已到,就會(huì)重置手動(dòng)故障轉(zhuǎn)移的狀態(tài),表示終止該過(guò)程。manualFailoverCheckTimeout函數(shù)代碼如下:

/* If a manual failover timed out, abort it. */ 
void manualFailoverCheckTimeout(void) { 
  if (server.cluster->mf_end  server.cluster->mf_end  mstime()) { 
    redisLog(REDIS_WARNING,"Manual failover timed out."); 
    resetManualFailover(); 
  } 
} 

二:從節(jié)點(diǎn)遷移

         在Redis集群中,為了增強(qiáng)集群的可用性,一般情況下需要為每個(gè)主節(jié)點(diǎn)配置若干從節(jié)點(diǎn)。但是這種主從關(guān)系如果是固定不變的,則經(jīng)過(guò)一段時(shí)間之后,就有可能出現(xiàn)孤立主節(jié)點(diǎn)的情況,也就是一個(gè)主節(jié)點(diǎn)再也沒(méi)有可用于故障轉(zhuǎn)移的從節(jié)點(diǎn)了,一旦這樣的主節(jié)點(diǎn)下線,整個(gè)集群也就不可用了。

         因此,在Redis集群中,增加了從節(jié)點(diǎn)遷移的功能。簡(jiǎn)單描述如下:一旦發(fā)現(xiàn)集群中出現(xiàn)了孤立主節(jié)點(diǎn),則某個(gè)從節(jié)點(diǎn)A就會(huì)自動(dòng)變成該孤立主節(jié)點(diǎn)的從節(jié)點(diǎn)。該從節(jié)點(diǎn)A滿足這樣的條件:A的主節(jié)點(diǎn)具有最多的附屬?gòu)墓?jié)點(diǎn);A在這些附屬?gòu)墓?jié)點(diǎn)中,節(jié)點(diǎn)ID是最小的(The acting slave is the slave among the masterswith the maximum number of attached slaves, that is not in FAIL state and hasthe smallest node ID)。

         該功能是在集群定時(shí)器函數(shù)clusterCron中實(shí)現(xiàn)的。這部分的代碼如下:

void clusterCron(void) { 
  ... 
  orphaned_masters = 0; 
  max_slaves = 0; 
  this_slaves = 0; 
  di = dictGetSafeIterator(server.cluster->nodes); 
  while((de = dictNext(di)) != NULL) { 
    clusterNode *node = dictGetVal(de); 
    now = mstime(); /* Use an updated time at every iteration. */ 
    mstime_t delay; 
 
    if (node->flags  
      (REDIS_NODE_MYSELF|REDIS_NODE_NOADDR|REDIS_NODE_HANDSHAKE)) 
        continue; 
 
    /* Orphaned master check, useful only if the current instance 
     * is a slave that may migrate to another master. */ 
    if (nodeIsSlave(myself)  nodeIsMaster(node)  !nodeFailed(node)) { 
      int okslaves = clusterCountNonFailingSlaves(node); 
 
      /* A master is orphaned if it is serving a non-zero number of 
       * slots, have no working slaves, but used to have at least one 
       * slave. */ 
      if (okslaves == 0  node->numslots > 0  node->numslaves) 
        orphaned_masters++; 
      if (okslaves > max_slaves) max_slaves = okslaves; 
      if (nodeIsSlave(myself)  myself->slaveof == node) 
        this_slaves = okslaves; 
    } 
    ... 
  } 
  ... 
  if (nodeIsSlave(myself)) { 
    ... 
    /* If there are orphaned slaves, and we are a slave among the masters 
     * with the max number of non-failing slaves, consider migrating to 
     * the orphaned masters. Note that it does not make sense to try 
     * a migration if there is no master with at least *two* working 
     * slaves. */ 
    if (orphaned_masters  max_slaves >= 2  this_slaves == max_slaves) 
      clusterHandleSlaveMigration(max_slaves); 
  } 
  ... 
}  

  輪訓(xùn)字典server.cluster->nodes,只要其中的節(jié)點(diǎn)不是當(dāng)前節(jié)點(diǎn),沒(méi)有處于REDIS_NODE_NOADDR或者握手狀態(tài),就對(duì)該node節(jié)點(diǎn)做相應(yīng)的處理:

         如果當(dāng)前節(jié)點(diǎn)是從節(jié)點(diǎn),并且node節(jié)點(diǎn)是主節(jié)點(diǎn),并且node未被標(biāo)記為下線,則首先調(diào)用函數(shù)clusterCountNonFailingSlaves,計(jì)算node節(jié)點(diǎn)未下線的從節(jié)點(diǎn)個(gè)數(shù)okslaves,如果node主節(jié)點(diǎn)的okslaves為0,并且該主節(jié)點(diǎn)負(fù)責(zé)的插槽數(shù)不為0,說(shuō)明該node主節(jié)點(diǎn)是孤立主節(jié)點(diǎn),因此增加orphaned_masters的值;如果該node主節(jié)點(diǎn)的okslaves大于max_slaves,則將max_slaves改為okslaves,因此,max_slaves記錄了所有主節(jié)點(diǎn)中,擁有最多未下線從節(jié)點(diǎn)的那個(gè)主節(jié)點(diǎn)的未下線從節(jié)點(diǎn)個(gè)數(shù);如果當(dāng)前節(jié)點(diǎn)正好是node主節(jié)點(diǎn)的從節(jié)點(diǎn)之一,則將okslaves記錄到this_slaves中,以上都是為后續(xù)做從節(jié)點(diǎn)遷移做的準(zhǔn)備;

         輪訓(xùn)完所有節(jié)點(diǎn)之后,如果存在孤立主節(jié)點(diǎn),并且max_slaves大于等于2,并且當(dāng)前節(jié)點(diǎn)剛好是那個(gè)擁有最多未下線從節(jié)點(diǎn)的主節(jié)點(diǎn)的眾多從節(jié)點(diǎn)之一,則調(diào)用函數(shù)clusterHandleSlaveMigration,滿足條件的情況下,進(jìn)行從節(jié)點(diǎn)遷移,也就是將當(dāng)前從節(jié)點(diǎn)置為某孤立主節(jié)點(diǎn)的從節(jié)點(diǎn)。

         clusterHandleSlaveMigration函數(shù)的代碼如下:

void clusterHandleSlaveMigration(int max_slaves) { 
  int j, okslaves = 0; 
  clusterNode *mymaster = myself->slaveof, *target = NULL, *candidate = NULL; 
  dictIterator *di; 
  dictEntry *de; 
  /* Step 1: Don't migrate if the cluster state is not ok. */ 
  if (server.cluster->state != REDIS_CLUSTER_OK) return; 
  /* Step 2: Don't migrate if my master will not be left with at least 
   *     'migration-barrier' slaves after my migration. */ 
  if (mymaster == NULL) return; 
  for (j = 0; j  mymaster->numslaves; j++) 
    if (!nodeFailed(mymaster->slaves[j])  
      !nodeTimedOut(mymaster->slaves[j])) okslaves++; 
  if (okslaves = server.cluster_migration_barrier) return; 
  /* Step 3: Idenitfy a candidate for migration, and check if among the 
   * masters with the greatest number of ok slaves, I'm the one with the 
   * smaller node ID. 
   * 
   * Note that this means that eventually a replica migration will occurr 
   * since slaves that are reachable again always have their FAIL flag 
   * cleared. At the same time this does not mean that there are no 
   * race conditions possible (two slaves migrating at the same time), but 
   * this is extremely unlikely to happen, and harmless. */ 
  candidate = myself; 
  di = dictGetSafeIterator(server.cluster->nodes); 
  while((de = dictNext(di)) != NULL) { 
    clusterNode *node = dictGetVal(de); 
    int okslaves; 
    /* Only iterate over working masters. */ 
    if (nodeIsSlave(node) || nodeFailed(node)) continue; 
    /* If this master never had slaves so far, don't migrate. We want 
     * to migrate to a master that remained orphaned, not masters that 
     * were never configured to have slaves. */ 
    if (node->numslaves == 0) continue; 
    okslaves = clusterCountNonFailingSlaves(node); 
    if (okslaves == 0  target == NULL  node->numslots > 0) 
      target = node; 
    if (okslaves == max_slaves) { 
      for (j = 0; j  node->numslaves; j++) { 
        if (memcmp(node->slaves[j]->name, 
              candidate->name, 
              REDIS_CLUSTER_NAMELEN)  0) 
        { 
          candidate = node->slaves[j]; 
        } 
      } 
    } 
  } 
  dictReleaseIterator(di); 
  /* Step 4: perform the migration if there is a target, and if I'm the 
   * candidate. */ 
  if (target  candidate == myself) { 
    redisLog(REDIS_WARNING,"Migrating to orphaned master %.40s", 
      target->name); 
    clusterSetMaster(target); 
  } 
} 

         如果當(dāng)前集群狀態(tài)不是REDIS_CLUSTER_OK,則直接返回;如果當(dāng)前從節(jié)點(diǎn)沒(méi)有主節(jié)點(diǎn),則直接返回;

         接下來(lái)計(jì)算,當(dāng)前從節(jié)點(diǎn)的主節(jié)點(diǎn),具有未下線從節(jié)點(diǎn)的個(gè)數(shù)okslaves;如果okslaves小于等于遷移閾值server.cluster_migration_barrier,則直接返回;

         接下來(lái),開(kāi)始輪訓(xùn)字典server.cluster->nodes,針對(duì)其中的每一個(gè)節(jié)點(diǎn)node:

         如果node節(jié)點(diǎn)是從節(jié)點(diǎn),或者處于下線狀態(tài),則直接處理下一個(gè)節(jié)點(diǎn);如果node節(jié)點(diǎn)沒(méi)有配置從節(jié)點(diǎn),則直接處理下一個(gè)節(jié)點(diǎn);

        調(diào)用clusterCountNonFailingSlaves函數(shù),計(jì)算該node節(jié)點(diǎn)的未下線主節(jié)點(diǎn)數(shù)okslaves;如果okslaves為0,并且該node節(jié)點(diǎn)的numslots大于0,說(shuō)明該主節(jié)點(diǎn)之前有從節(jié)點(diǎn),但是都下線了,因此找到了一個(gè)孤立主節(jié)點(diǎn)target;

         如果okslaves等于參數(shù)max_slaves,說(shuō)明該node節(jié)點(diǎn)就是具有最多未下線從節(jié)點(diǎn)的主節(jié)點(diǎn),因此將當(dāng)前節(jié)點(diǎn)的節(jié)點(diǎn)ID,與其所有從節(jié)點(diǎn)的節(jié)點(diǎn)ID進(jìn)行比較,如果當(dāng)前節(jié)點(diǎn)的名字更大,則將candidate置為具有更小名字的那個(gè)從節(jié)點(diǎn);(其實(shí)從這里就可以直接退出返回了)

         輪訓(xùn)完所有節(jié)點(diǎn)后,如果找到了孤立節(jié)點(diǎn),并且當(dāng)前節(jié)點(diǎn)擁有最小的節(jié)點(diǎn)ID,則調(diào)用clusterSetMaster,將target置為當(dāng)前節(jié)點(diǎn)的主節(jié)點(diǎn),并開(kāi)始主從復(fù)制流程。

三:configEpoch沖突問(wèn)題

         在集群中,負(fù)責(zé)不同槽位的主節(jié)點(diǎn),具有相同的configEpoch其實(shí)是沒(méi)有問(wèn)題的,但是有可能因?yàn)槿藶榻槿氲脑蚧蛘連UG的問(wèn)題,導(dǎo)致具有相同configEpoch的主節(jié)點(diǎn)都宣稱(chēng)負(fù)責(zé)相同的槽位,這在分布式系統(tǒng)中是致命的問(wèn)題;因此,Redis規(guī)定集群中的所有節(jié)點(diǎn),必須具有不同的configEpoch。

         當(dāng)某個(gè)從節(jié)點(diǎn)升級(jí)為新主節(jié)點(diǎn)時(shí),它會(huì)得到一個(gè)大于當(dāng)前所有節(jié)點(diǎn)的configEpoch的新configEpoch,所以不會(huì)導(dǎo)致具有重復(fù)configEpoch的從節(jié)點(diǎn)(因?yàn)橐淮芜x舉中,不會(huì)有兩個(gè)從節(jié)點(diǎn)同時(shí)勝出)。但是在管理員發(fā)起的重新分片過(guò)程的最后,遷入槽位的節(jié)點(diǎn)會(huì)自己更新自己的configEpoch,而無(wú)需其他節(jié)點(diǎn)的同意;或者手動(dòng)強(qiáng)制故障轉(zhuǎn)移過(guò)程,也會(huì)導(dǎo)致從節(jié)點(diǎn)在無(wú)需其他節(jié)點(diǎn)同意的情況下更新configEpoch,以上的情況都可能導(dǎo)致出現(xiàn)多個(gè)主節(jié)點(diǎn)具有相同configEpoch的情況。

         因此,就需要一種算法,保證集群中所有節(jié)點(diǎn)的configEpoch都不相同。這種算法是這樣實(shí)現(xiàn)的:當(dāng)某個(gè)主節(jié)點(diǎn)收到其他主節(jié)點(diǎn)發(fā)來(lái)的心跳包后,發(fā)現(xiàn)包中的configEpoch與自己的configEpoch相同,就會(huì)調(diào)用clusterHandleConfigEpochCollision函數(shù),解決這種configEpoch沖突的問(wèn)題。

         clusterHandleConfigEpochCollision函數(shù)的代碼如下:

void clusterHandleConfigEpochCollision(clusterNode *sender) { 
  /* Prerequisites: nodes have the same configEpoch and are both masters. */ 
  if (sender->configEpoch != myself->configEpoch || 
    !nodeIsMaster(sender) || !nodeIsMaster(myself)) return; 
  /* Don't act if the colliding node has a smaller Node ID. */ 
  if (memcmp(sender->name,myself->name,REDIS_CLUSTER_NAMELEN) = 0) return; 
  /* Get the next ID available at the best of this node knowledge. */ 
  server.cluster->currentEpoch++; 
  myself->configEpoch = server.cluster->currentEpoch; 
  clusterSaveConfigOrDie(1); 
  redisLog(REDIS_VERBOSE, 
    "WARNING: configEpoch collision with node %.40s." 
    " configEpoch set to %llu", 
    sender->name, 
    (unsigned long long) myself->configEpoch); 
} 

 如果發(fā)送節(jié)點(diǎn)的configEpoch不等于當(dāng)前節(jié)點(diǎn)的configEpoch,或者發(fā)送節(jié)點(diǎn)不是主節(jié)點(diǎn),或者當(dāng)前節(jié)點(diǎn)不是主節(jié)點(diǎn),則直接返回;

        如果相比于當(dāng)前節(jié)點(diǎn)的節(jié)點(diǎn)ID,發(fā)送節(jié)點(diǎn)的節(jié)點(diǎn)ID更小,則直接返回;

        因此,較小名字的節(jié)點(diǎn)能獲得更大的configEpoch,接下來(lái)首先增加自己的currentEpoch,然后將configEpoch賦值為currentEpoch。

         這樣,即使有多個(gè)節(jié)點(diǎn)具有相同的configEpoch,最終,只有具有最大節(jié)點(diǎn)ID的節(jié)點(diǎn)的configEpoch保持不變,其他節(jié)點(diǎn)都會(huì)增加自己的configEpoch,而且增加的值會(huì)不同,具有最小NODE ID的節(jié)點(diǎn),最終具有最大的configEpoch。

總結(jié)

以上就是本文關(guān)于Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解的全部?jī)?nèi)容,感興趣的朋友可以參閱:詳細(xì)分析Redis集群故障、簡(jiǎn)述Redis和MySQL的區(qū)別、Spring AOP實(shí)現(xiàn)Redis緩存數(shù)據(jù)庫(kù)查詢?cè)创a等,有不足之處,請(qǐng)留言指出,感謝朋友們對(duì)本站的支持!

您可能感興趣的文章:
  • 從MySQL到Redis的簡(jiǎn)單數(shù)據(jù)庫(kù)遷移方法
  • Redis數(shù)據(jù)導(dǎo)入導(dǎo)出以及數(shù)據(jù)遷移的4種方法詳解
  • php實(shí)現(xiàn)redis數(shù)據(jù)庫(kù)指定庫(kù)號(hào)遷移的方法
  • Redis migrate數(shù)據(jù)遷移工具的使用教程

標(biāo)簽:贛州 林芝 景德鎮(zhèn) 香港 澳門(mén) 唐山 廣東 揚(yáng)州

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解》,本文關(guān)鍵詞  Redis,源碼,解析,集群,手動(dòng),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解》相關(guān)的同類(lèi)信息!
  • 本頁(yè)收集關(guān)于Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    成人欧美一区二区三区| 日韩视频免费观看高清完整版在线观看| 欧美va亚洲va香蕉在线| av欧美精品.com| 国产精品夜夜嗨| 激情av综合网| 精品无人码麻豆乱码1区2区| 亚州成人在线电影| 亚洲天堂中文字幕| 国产精品视频观看| 国产日韩欧美亚洲| 国产人成一区二区三区影院| 精品国产sm最大网站免费看| 日韩一区二区电影网| 欧美二区三区91| 日韩一二三四区| 欧美精品一区二区精品网| 久久影院电视剧免费观看| 精品国产电影一区二区| 久久综合色婷婷| 久久久久久久久一| 欧美高清在线精品一区| 国产精品久久久久久久久搜平片| 国产精品乱人伦| 亚洲人成网站在线| 亚洲午夜久久久久久久久久久| 一区二区理论电影在线观看| 午夜精品福利一区二区蜜股av | 91福利国产精品| 一本到三区不卡视频| 欧美无人高清视频在线观看| 欧美日韩国产美女| 欧美变态凌虐bdsm| 国产拍揄自揄精品视频麻豆| 中文字幕日韩一区二区| 亚洲图片有声小说| 偷拍日韩校园综合在线| 久久国产夜色精品鲁鲁99| 精品在线播放免费| www.亚洲国产| 欧美精选在线播放| 久久久久久日产精品| 国产精品久久久久久久久免费樱桃 | 精品日韩99亚洲| 久久久夜色精品亚洲| 亚洲视频在线一区| 久久精品国产免费看久久精品| 国产成a人亚洲| 欧美日韩一区国产| 国产日韩视频一区二区三区| 亚洲综合在线电影| 国产一区二区中文字幕| 95精品视频在线| 精品国产免费人成电影在线观看四季 | 日韩亚洲欧美在线观看| 欧美激情自拍偷拍| 日本强好片久久久久久aaa| av亚洲精华国产精华| 欧美不卡视频一区| 亚洲制服丝袜av| 懂色av一区二区在线播放| 欧美肥妇free| 亚洲国产岛国毛片在线| 麻豆国产精品视频| 欧美日韩一区二区三区高清| 中文字幕成人av| 免费欧美日韩国产三级电影| 色先锋久久av资源部| 久久精品欧美一区二区三区不卡| 亚洲一区二区四区蜜桃| 99久久婷婷国产综合精品电影| 日韩欧美电影一二三| 五月综合激情婷婷六月色窝| 97国产一区二区| 国产精品欧美综合在线| 九一九一国产精品| 欧美一区二区在线免费观看| 亚洲最大成人网4388xx| 成人av资源在线| 国产无人区一区二区三区| 免费观看久久久4p| 欧美一级欧美一级在线播放| 性欧美大战久久久久久久久| 99久久精品99国产精品| 国产精品热久久久久夜色精品三区| 麻豆精品在线观看| 日韩一级片网站| 另类小说图片综合网| 日韩视频在线你懂得| 美腿丝袜亚洲色图| 日韩一区二区麻豆国产| 蜜臀av性久久久久蜜臀aⅴ流畅| 欧美日韩精品欧美日韩精品| 性感美女久久精品| 欧美日韩国产片| 日本aⅴ精品一区二区三区| 欧美巨大另类极品videosbest| 偷拍日韩校园综合在线| 欧美一区二区三区在| 看电影不卡的网站| 久久九九影视网| 成人性色生活片免费看爆迷你毛片| 久久久久99精品一区| 国产成人精品午夜视频免费| 久久久久久久久久久黄色| 国产在线播放一区二区三区| 国产三级欧美三级日产三级99| 成人一二三区视频| 亚洲天堂精品在线观看| 欧美午夜片在线看| 日本不卡高清视频| 国产欧美日韩精品在线| 色综合久久综合中文综合网| 亚洲国产视频一区二区| 欧美一级午夜免费电影| 国产成人精品www牛牛影视| 中文字幕中文字幕一区二区| 欧美在线短视频| 激情欧美一区二区| 亚洲色图在线播放| 日韩欧美中文字幕公布| 风间由美中文字幕在线看视频国产欧美 | 日韩精品一区二区三区蜜臀| 92精品国产成人观看免费| 亚洲一二三区不卡| 丝袜美腿亚洲一区| 国内精品久久久久影院色| 国产精品三级视频| 欧美另类videos死尸| 国产大片一区二区| 夜夜夜精品看看| 久久综合久久综合久久| 99精品久久只有精品| 日本不卡123| 亚洲色图丝袜美腿| 久久综合九色欧美综合狠狠| 精品视频一区二区三区免费| 狠狠色综合色综合网络| 亚洲免费观看高清完整| 精品国产一区二区精华| 欧美日韩精品系列| 成人av电影免费观看| 另类小说综合欧美亚洲| 亚洲一区影音先锋| 国产精品电影院| 精品欧美久久久| 欧美日免费三级在线| 成人av在线影院| 国产精品自在欧美一区| 免费在线看一区| 亚洲风情在线资源站| 国产精品拍天天在线| 26uuu亚洲| 日韩欧美电影一二三| 91极品视觉盛宴| 99视频在线精品| 国产一区在线看| 免费成人在线网站| 肉色丝袜一区二区| 亚洲国产人成综合网站| 一区二区高清视频在线观看| 亚洲天堂av一区| 中文字幕一区二区三区不卡| 久久久99精品免费观看不卡| 5858s免费视频成人| 欧美性感一类影片在线播放| 91色婷婷久久久久合中文| 国产xxx精品视频大全| 国产中文一区二区三区| 久久不见久久见免费视频7| 免费欧美高清视频| 蜜桃一区二区三区在线| 日韩av中文在线观看| 日韩1区2区日韩1区2区| 蜜臀av国产精品久久久久| 青青草国产成人99久久| 久久精品99国产国产精| 激情综合色丁香一区二区| 久久精品国产澳门| 国产精品白丝av| 成人性色生活片| 北条麻妃一区二区三区| 99久久99久久精品国产片果冻| 成人av电影在线| 欧美性一区二区| 日韩美女天天操| 亚洲国产精品av| 亚洲一区二区五区| 日韩不卡在线观看日韩不卡视频| 免播放器亚洲一区| 国内精品视频666| 国产91在线看| 色哦色哦哦色天天综合| 欧美日韩亚洲综合| 精品久久久久久久人人人人传媒| 久久免费偷拍视频| 国产精品蜜臀在线观看| 亚洲成a天堂v人片| 国产永久精品大片wwwapp| 99精品视频在线播放观看|