成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redis 集群中如何處理非本節(jié)點的 slot

數(shù)據(jù)庫 Redis
這篇文章就來聊聊當(dāng)某個節(jié)點處理到非它所負(fù)責(zé)的slot時是如何處理的,這一點很好的體現(xiàn)了redis對于raft協(xié)議良好的設(shè)計與實現(xiàn)。

我們都知道redis集群有16384個槽,它會因為我們集群個數(shù)配置的不同而分配不同的slot給各個節(jié)點,而這篇文章就來聊聊當(dāng)某個節(jié)點處理到非它所負(fù)責(zé)的slot時是如何處理的,這一點很好的體現(xiàn)了redis對于raft協(xié)議良好的設(shè)計與實現(xiàn)。

一、詳解redis集群指令處理

1. 整體流程

假設(shè)我們現(xiàn)在集群中有個節(jié)點,每個節(jié)點各自負(fù)責(zé)一部分槽,此時我們的客戶端向節(jié)點2發(fā)起一個set指令,而該指令對應(yīng)的key應(yīng)該是要存放到節(jié)點1中,對此節(jié)點2的做法是查看自己所維護(hù)的節(jié)點列表是否有負(fù)責(zé)該slot的節(jié)點,如果發(fā)現(xiàn)了而回復(fù)給客戶端move指令,告知客戶端到指令的ip端口的節(jié)點進(jìn)行鍵值對存儲:

了解完整體流程之后,我們通過源碼的方式來印證這些實現(xiàn)上的細(xì)節(jié),我們都知道redis客戶端發(fā)送的指令都會被redis的processCommand處理,該函數(shù)如果發(fā)現(xiàn)當(dāng)前是以集群的方式啟動并且符合以下兩個條件則以集群的邏輯解析這條指令:

  • 發(fā)送指令的不是master服務(wù)器。
  • 參數(shù)中帶有key。

那么redis就會調(diào)用getNodeByQuery查詢重定向的節(jié)點,如果發(fā)現(xiàn)查詢到的節(jié)點不是自己或者為空則調(diào)用clusterRedirectClient進(jìn)行重定向處理:

int processCommand(redisClient *c) {
    //......
    //如果開啟了集群,且發(fā)送者不是master且參數(shù)帶key則步入邏輯
    if (server.cluster_enabled &&
        !(c->flags & REDIS_MASTER) &&
        !(c->flags & REDIS_LUA_CLIENT &&
          server.lua_caller->flags & REDIS_MASTER) &&
        !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0))
    {
        int hashslot;

        if (server.cluster->state != REDIS_CLUSTER_OK) {
         //......
        } else {
            int error_code;
            //查找可以處理的節(jié)點
            clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&error_code);
            //如果為空且或者非自己,則調(diào)用clusterRedirectClient進(jìn)行重定向
            if (n == NULL || n != server.cluster->myself) {
                flagTransaction(c);
                clusterRedirectClient(c,n,hashslot,error_code);
                return REDIS_OK;
            }
        }
    }

   //......
   //處理當(dāng)前請求指令并返回
}

2. 詳解節(jié)點定位步驟getNodeByQuery

步入getNodeByQuery即可看到查詢的核心流程,無論是單條還是多條客戶端指令,他都會封裝成multiState結(jié)構(gòu)體交由后續(xù)邏輯處理,而后續(xù)邏輯就會遍歷這些指令并計算出對應(yīng)的slot,然后執(zhí)行如下邏輯:

  • 如果發(fā)現(xiàn)定位到的節(jié)點是自己,且當(dāng)前節(jié)點正在做遷移,則做個遷移標(biāo)記,然后檢查當(dāng)前節(jié)點是否有這個槽,如果沒有則發(fā)送ASK指令告知客戶端重定向到另一個遷移的目標(biāo)槽試試看。
  • 如果對應(yīng)的key沒有找到對應(yīng)的槽,則直接返回當(dāng)前節(jié)點。
  • 找到目標(biāo)槽,直接返回MOVE指令和目標(biāo)槽的信息。

對應(yīng)我們給出getNodeByQuery的核心代碼段:

clusterNode *getNodeByQuery(redisClient *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *error_code) {
    //......
    //如果是exec命令則用客戶端的multiState封裝這些命令
    if (cmd->proc == execCommand) {
        /* If REDIS_MULTI flag is not set EXEC is just going to return an
         * error. */
        if (!(c->flags & REDIS_MULTI)) return myself;
        ms = &c->mstate;
    } else {
       //如果不是exec則自己創(chuàng)建一個multiState封裝這單條指令保證后續(xù)邏輯一致
        ms = &_ms;
        _ms.commands = &mc;
        //命令個數(shù)1
        _ms.count = 1;
        //命令參數(shù)
        mc.argv = argv;
        //命令參數(shù)個數(shù)
        mc.argc = argc;
        //對應(yīng)的命令
        mc.cmd = cmd;
    }

    
    //遍歷multiState中的命令
    for (i = 0; i < ms->count; i++) {
        struct redisCommand *mcmd;
        robj **margv;
        int margc, *keyindex, numkeys, j;
  //解析出命令、參數(shù)個數(shù)、參數(shù)
        mcmd = ms->commands[i].cmd;
        margc = ms->commands[i].argc;
        margv = ms->commands[i].argv;
        //解析出key以及個數(shù)
        keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);
        for (j = 0; j < numkeys; j++) {
            //拿到key
            robj *thiskey = margv[keyindex[j]];
            //計算slot
            int thisslot = keyHashSlot((char*)thiskey->ptr,
                                       sdslen(thiskey->ptr));

            if (firstkey == NULL) {
             
                firstkey = thiskey;
                slot = thisslot;
                //拿著計算的slot定位到對應(yīng)的節(jié)點
                n = server.cluster->slots[slot];

                
                //如果定位到的節(jié)點就是當(dāng)前節(jié)點正在做遷出或者遷入,則migrating_slot/importing_slot設(shè)置為1
                if (n == myself &&
                    server.cluster->migrating_slots_to[slot] != NULL)
                {
                    migrating_slot = 1;
                } else if (server.cluster->importing_slots_from[slot] != NULL) {
                    importing_slot = 1;
                }
            } else {
               //......
            }

           
            //如果正在做遷出或者嵌入,且當(dāng)前找不到當(dāng)前db找不到key的位置,則missing_keys++意為某個key可能正在被遷移中所以沒有命中
            if ((migrating_slot || importing_slot) &&
                lookupKeyRead(&server.db[0],thiskey) == NULL)
            {
                missing_keys++;
            }
        }
        getKeysFreeResult(keyindex);
    }

   
    //所有key都沒有對應(yīng)節(jié)點,直接返回當(dāng)前節(jié)點
    if (n == NULL) return myself;

    //......
    //正在遷出且這個key在當(dāng)前節(jié)點沒有被命中,則將error_code設(shè)置為ask,并返回遷出的節(jié)點信息,告知客戶端到返回節(jié)點嘗試指令
    if (migrating_slot && missing_keys) {
        if (error_code) *error_code = REDIS_CLUSTER_REDIR_ASK;
        return server.cluster->migrating_slots_to[slot];
    }

    //......
    //返回其他節(jié)點,error_code設(shè)置為move
    if (n != myself && error_code) *error_code = REDIS_CLUSTER_REDIR_MOVED;
    return n;
}

3. 結(jié)果告知客戶端

上述流程發(fā)現(xiàn)處理的節(jié)點不是自己之后,調(diào)用clusterRedirectClient進(jìn)行重定向,如果是REDIS_CLUSTER_REDIR_MOVED則告知客戶端這些slot后續(xù)直接找重定向節(jié)點處理就好了,后續(xù)無需找自己。若是REDIS_CLUSTER_REDIR_ASK則說明當(dāng)前節(jié)點正處于數(shù)據(jù)遷移到目標(biāo)節(jié)點,你可以到遷移的節(jié)點進(jìn)行請求,后續(xù)再次發(fā)起請求是還是找當(dāng)前節(jié)點看看能否出去,如果不能在進(jìn)行重定向:

void clusterRedirectClient(redisClient *c, clusterNode *n, int hashslot, int error_code) {
    //......
    if(......){
    //......
    } else if (error_code == REDIS_CLUSTER_REDIR_MOVED ||
               error_code == REDIS_CLUSTER_REDIR_ASK)
    {
        //返回move命令告知要移動到的節(jié)點后續(xù)直接到move的,如果是ask則返回正在遷往的節(jié)點地址,是臨時措施,下次客戶端還會找當(dāng)前節(jié)點
        addReplySds(c,sdscatprintf(sdsempty(),
            "-%s %d %s:%d\r\n",
            (error_code == REDIS_CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",
            hashslot,n->ip,n->port));
    } else {
        redisPanic("getNodeByQuery() unknown error.");
    }
}

二、小結(jié)

這篇文章比較精簡,我們通過源碼的方式簡單的剖析了去中心化的redis如何在不同節(jié)點處理不同槽的請求,大體過程比較簡單:

  • 接收并處理客戶端傳入的key指令操作。
  • 通過getNodeByQuery獲取key對應(yīng)的slot所屬節(jié)點。
  • 如果是當(dāng)前節(jié)點的slot直接處理。
  • 如果不是則查看是否正在遷出,如果是則返回ask讓客戶端到別的節(jié)點試試看,反之進(jìn)入步驟5。
  • 如果定位的slot對應(yīng)的節(jié)點是別的節(jié)點則直接用move指令重定向客戶端,讓客戶端到另一個節(jié)點詢問結(jié)果。
責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2024-09-11 20:05:56

2024-04-16 13:32:57

2024-12-25 10:24:31

2023-03-09 12:21:38

2018-11-21 12:27:21

JavaScript 貨幣值區(qū)域

2019-11-08 08:00:00

ASP .NETASP .NET Cocookie

2011-04-11 17:10:16

Oracle

2012-07-30 09:35:10

項目管理

2020-05-18 10:52:10

集群SessionRedis

2019-08-15 10:20:19

云計算技術(shù)安全

2021-03-24 10:40:26

Python垃圾語言

2025-07-01 01:55:00

Redis集群模式

2013-03-20 11:01:37

Redis客戶端連接

2021-08-18 08:20:14

SQL除數(shù)統(tǒng)計

2011-01-21 16:27:43

NagiosSendmail

2022-08-08 13:45:12

Redis面試Hash

2017-10-26 08:43:18

JavaScript內(nèi)存處理

2019-12-23 10:20:12

Web圖片優(yōu)化前端

2021-03-01 07:31:53

消息支付高可用

2023-04-06 15:19:51

點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 亚洲一二三区在线观看 | 九九九色| 成人在线观看网站 | 激情五月激情综合网 | 国产一区二区三区四区hd | 一级看片免费视频囗交动图 | 成人国产精品色哟哟 | 日韩精品免费看 | 中文字幕91av | 国产成人在线视频免费观看 | 日本午夜精品一区二区三区 | 美女啪啪国产 | 久久精品久久久久久 | 国产精品亚洲欧美日韩一区在线 | 91在线免费视频 | 成人综合视频在线 | 91精品国产777在线观看 | 亚洲国产精品一区二区久久 | 人人人人干 | 日韩精品1区2区3区 爱爱综合网 | 亚洲精品免费视频 | 九一国产精品 | 免费成人在线网站 | 亚洲国产精品日韩av不卡在线 | 欧美亚洲另类在线 | 激情欧美日韩一区二区 | 国产精品美女久久久av超清 | 在线视频亚洲 | 久久国产视频播放 | 亚洲欧美综合精品久久成人 | 黄色国产 | 国产麻豆一区二区三区 | 亚洲激情网站 | 国产精品一区二区在线 | 欧美日本韩国一区二区 | 欧美视频在线播放 | 激情六月丁香婷婷 | 精品久| 毛片一区二区 | 91精品久久久久久久久久入口 | 成人一区二区三区在线观看 |