自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Redis 哨兵是如何完成初始化的

數(shù)據(jù)庫 Redis 安全
本文筆者將從源碼分析的角度介紹一下 Redis 哨兵是如何完成初始化的,并對 Redis 哨兵的啟動步驟做了簡單總結(jié)。

本系列終于更新到哨兵模塊的介紹,由于哨兵模塊涉及節(jié)點通信和選舉等流程,所以筆者將其分為3個篇章進行剖析,而本文筆者將從源碼分析的角度介紹一下redis哨兵是如何完成初始化的。

詳解哨兵初始化流程

1. 哨兵基本數(shù)據(jù)結(jié)構(gòu)

哨兵通過raft協(xié)議實現(xiàn)leader選舉和故障轉(zhuǎn)移線,針對這樣一個場景,我們的哨兵一般會使用單數(shù)個,為了保證選舉的正常進行哨兵還需要記錄節(jié)一次每次進行選舉的信息維護:

  • 通過current_epoch記錄當(dāng)前選舉的紀(jì)元。
  • 用masters指針?biāo)赶虻淖值渚S護當(dāng)前哨兵監(jiān)聽的master節(jié)點信息,每個master都會以sentinelRedisInstance結(jié)構(gòu)體進行信息維護各自的name、slave等信息。
  • 通過announce_ip和announce_port用于和其他哨兵聯(lián)系時提供自身的地址信息。

對此我們給出sentinel 的結(jié)構(gòu)體代碼,讀者可參考上述的介紹了解一下每一個核心字段:

struct sentinelState {
    //當(dāng)前紀(jì)元
    uint64_t current_epoch;     /* Current epoch. */
    //維護主節(jié)點的哈希表指針
    dict *masters;      /* Dictionary of master sentinelRedisInstances.
    //......
    //向其他哨兵發(fā)送當(dāng)前實例的地址信息
    char *announce_ip;      /* IP addr that is gossiped to other sentinels if
                               not NULL. */
    int announce_port;      /* Port that is gossiped to other sentinels if
                               non zero. */
} sentinel;

2. 初始化哨兵基本配置

redis在啟動會檢查本次啟動是否是通過redis-sentinel指令或者--sentinel參數(shù)啟動哨兵,如果是則按照哨兵模式進行初始化,默認(rèn)給該節(jié)點端口號為26379并初始化哨兵sentinel:

對應(yīng)的我們給出核心代碼段,可以看到main方法啟動后會檢查是否是通過redis-sentinel或者參數(shù)--sentinel啟動,如果是則將sentinel_mode 設(shè)置為1,完成后續(xù)的配置和結(jié)構(gòu)體初始化:

int main(int argc, char **argv) {
   //......
   //檢查使用通過
    server.sentinel_mode = checkForSentinelMode(argc,argv);
    //......
    if (server.sentinel_mode) {
        initSentinelConfig();//初始化哨兵配置
        initSentinel();//初始化哨兵結(jié)構(gòu)體
    }
 //......
}

我們步入initSentinelConfig方法可以看到配置初始化只做了一件事,即將端口號設(shè)置為26379:

void initSentinelConfig(void) {
 //將端口號設(shè)置為26379
    server.port = REDIS_SENTINEL_PORT;
}

我們再查看initSentinel這個初始化哨兵結(jié)構(gòu)體的函數(shù),可以看到其內(nèi)部會將當(dāng)前server執(zhí)行的命令表改為哨兵的命令,以及將所有IP、端口、masters指針進行初始化:

/* Perform the Sentinel mode initialization. */
void initSentinel(void) {
    unsigned int j;

   
    //將哨兵模式的命令表改為哨兵專用命令表
    dictEmpty(server.commands,NULL);
    for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
        int retval;
        struct redisCommand *cmd = sentinelcmds+j;

        retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);
        redisAssert(retval == DICT_OK);
    }

    //紀(jì)元初始化
    sentinel.current_epoch = 0;
    //masters指針初始化
    sentinel.masters = dictCreate(&instancesDictType,NULL);
   //......
   //ip和端口號初始化
    sentinel.announce_ip = NULL;
    sentinel.announce_port = 0;
}

3. 初始化masters字典表

經(jīng)歷了上一步的初始化之后,redis就會開始解析redis.conf文件中解析出所有的master信息并存入masters中,假設(shè)我們在conf文件中鍵入如下配置:

# sentinel   monitor <name> <host> <port> <quorum>
sentinel monitor masters-1 192.168.0.128  6379  1

redis就會從配置文件中匹配到sentinel 這個代碼段,然后解析出<name> <host> <port> <quorum>這幾個參數(shù),生成一個master即可sentinelRedisInstance對象,存入masters這個字典中:

我們給出讀取redis配置的核心代碼段

void loadServerConfigFromString(char *config) {
    //......

    for (i = 0; i < totlines; i++) {
        sds *argv;
        int argc;

        linenum = i+1;
        lines[i] = sdstrim(lines[i]," \t\r\n");

        /* Skip comments and blank lines */
        if (lines[i][0] == '#' || lines[i][0] == '\0') continue;

        /* Split into arguments */
        argv = sdssplitargs(lines[i],&argc);
        if (argv == NULL) {
            err = "Unbalanced quotes in configuration line";
            goto loaderr;
        }

        /* Skip this line if the resulting command vector is empty. */
        if (argc == 0) {
            sdsfreesplitres(argv,argc);
            continue;
        }
        sdstolower(argv[0]);

        /* Execute config directives */
        if (!strcasecmp(argv[0],"timeout") && argc == 2) {
         //......
         } else if (!strcasecmp(argv[0],"sentinel")) {//如果匹配到sentinel
              //......
             //解析參數(shù)生成master信息存入哨兵的masters字典表中
                err = sentinelHandleConfiguration(argv+1,argc-1);
                if (err) goto loaderr;
            }
        }   //......
    }

     //......
}

我們再次步入sentinelHandleConfiguration可以看到大量配置參數(shù)解析的邏輯,流程比較簡單就是字符串處理,我們就以本次的監(jiān)聽主節(jié)點的命令monitor為例,當(dāng)redis解析到這個關(guān)鍵字則調(diào)用createSentinelRedisInstance解析出conf文件配置的master信息存入字典中:

char *sentinelHandleConfiguration(char **argv, int argc) {
    sentinelRedisInstance *ri;

    if (!strcasecmp(argv[0],"monitor") && argc == 5) {
        /* monitor <name> <host> <port> <quorum> */
        int quorum = atoi(argv[4]);

        if (quorum <= 0) return "Quorum must be 1 or greater.";
        //解析出master信息存入字典中,可以看到傳入的標(biāo)識為SRI_MASTER,即當(dāng)前解析并監(jiān)視的對象是master節(jié)點
        if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2],
                                        atoi(argv[3]),quorum,NULL) == NULL)
        {
            //......
        }
    } 
       //......
}

最終我們步入createSentinelRedisInstance即可看到該方法通過與運算匹配出當(dāng)前傳入的信息是master的,于是拿到哨兵的masters字典表,完成master信息解析后將其存入字典中:

sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *hostname, int port, int quorum, sentinelRedisInstance *master) {
    //......
    //基于與運算獲得哨兵的masters表
    if (flags & SRI_MASTER) table = sentinel.masters;
    else if (flags & SRI_SLAVE) table = master->slaves;
    else if (flags & SRI_SENTINEL) table = master->sentinels;
    //......
   //創(chuàng)建master實例
    ri = zmalloc(sizeof(*ri));
  //......
    ri->name = sdsname;
   //......

    //存入哨兵的字典表masters中
    dictAdd(table, ri->name, ri);
    return ri;
}

4. 啟動并監(jiān)聽master

完成上述步驟后,redis得知當(dāng)前節(jié)點是以哨兵模式啟動,于是調(diào)用sentinelIsRunning方法,內(nèi)部遍歷masters節(jié)點的信息,發(fā)送到monitor頻道告知其他當(dāng)前哨兵監(jiān)聽的所有monitor信息

我們從入口看起,可以看到main方法后續(xù)會判斷如果是哨兵模式則執(zhí)行sentinelIsRunning:

if (!server.sentinel_mode) {
    //......
    } else {//如果是哨兵模式則如此啟動哨兵
        sentinelIsRunning();
    }

其內(nèi)部調(diào)用sentinelGenerateInitialMonitorEvents遍歷masters表的信息將master發(fā)布到monitor頻道上:

void sentinelIsRunning(void) {
    //......
    //獲取masters迭代器對所有主節(jié)點設(shè)置monitor
    sentinelGenerateInitialMonitorEvents();
}

查看sentinelGenerateInitialMonitorEvents邏輯就是遍歷masters表獲取master信息調(diào)用sentinelEvent向主節(jié)點master的monitor頻道上發(fā)布消息告知當(dāng)前哨兵開始監(jiān)控:

void sentinelGenerateInitialMonitorEvents(void) {
    dictIterator *di;
    dictEntry *de;

    di = dictGetIterator(sentinel.masters);
    //遍歷master節(jié)點
    while((de = dictNext(di)) != NULL) {
        sentinelRedisInstance *ri = dictGetVal(de);
        //發(fā)布監(jiān)聽事件
        sentinelEvent(REDIS_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum);
    }
    dictReleaseIterator(di);
}

小結(jié)

我們簡單小結(jié)一下redis哨兵的啟動步驟:

  • redis-server感知到啟動模式為哨兵模式,則按照哨兵模式進行實例初始化。
  • 加載哨兵模式支持的操作指令。
  • 解析redis.conf配置中所有master信息存儲到哨兵實例結(jié)構(gòu)體的masters字典中。
  • 遍歷所有需要監(jiān)控的master,向這些master的monitor頻道發(fā)布monitor事件。
  • 自此當(dāng)前哨兵實例節(jié)點就開始監(jiān)聽主節(jié)點。
責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2021-01-26 09:14:19

Linux內(nèi)核模塊

2022-11-15 20:48:41

Linux

2023-11-12 23:08:17

C++初始化

2009-09-02 16:52:55

C#數(shù)組初始化

2011-03-16 10:52:20

2009-06-10 16:17:00

Netbeans JT初始化

2012-03-13 13:38:42

Java

2021-07-07 05:00:17

初始化源碼

2019-11-04 13:50:36

Java數(shù)組編程語言

2009-09-08 09:48:34

LINQ初始化數(shù)組

2009-11-11 15:29:15

ADO初始化

2010-07-28 10:22:33

FlexApplica

2022-07-06 10:37:45

SpringServlet初始化

2020-12-03 09:50:52

容器IoC流程

2021-03-12 10:30:11

SpringMVC流程初始化

2011-06-17 15:29:44

C#對象初始化器集合初始化器

2020-11-23 14:22:17

代碼Go存儲

2010-09-08 14:49:09

藍(lán)牙協(xié)議棧

2012-05-23 12:46:53

JavaJava類

2023-10-06 20:57:52

C++聚合成員
點贊
收藏

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