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

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

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

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

詳解哨兵初始化流程

1. 哨兵基本數據結構

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

  • 通過current_epoch記錄當前選舉的紀元。
  • 用masters指針所指向的字典維護當前哨兵監聽的master節點信息,每個master都會以sentinelRedisInstance結構體進行信息維護各自的name、slave等信息。
  • 通過announce_ip和announce_port用于和其他哨兵聯系時提供自身的地址信息。

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

struct sentinelState {
    //當前紀元
    uint64_t current_epoch;     /* Current epoch. */
    //維護主節點的哈希表指針
    dict *masters;      /* Dictionary of master sentinelRedisInstances.
    //......
    //向其他哨兵發送當前實例的地址信息
    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參數啟動哨兵,如果是則按照哨兵模式進行初始化,默認給該節點端口號為26379并初始化哨兵sentinel:

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

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

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

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

我們再查看initSentinel這個初始化哨兵結構體的函數,可以看到其內部會將當前server執行的命令表改為哨兵的命令,以及將所有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);
    }

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

3. 初始化masters字典表

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

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

redis就會從配置文件中匹配到sentinel 這個代碼段,然后解析出<name> <host> <port> <quorum>這幾個參數,生成一個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
              //......
             //解析參數生成master信息存入哨兵的masters字典表中
                err = sentinelHandleConfiguration(argv+1,argc-1);
                if (err) goto loaderr;
            }
        }   //......
    }

     //......
}

我們再次步入sentinelHandleConfiguration可以看到大量配置參數解析的邏輯,流程比較簡單就是字符串處理,我們就以本次的監聽主節點的命令monitor為例,當redis解析到這個關鍵字則調用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信息存入字典中,可以看到傳入的標識為SRI_MASTER,即當前解析并監視的對象是master節點
        if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2],
                                        atoi(argv[3]),quorum,NULL) == NULL)
        {
            //......
        }
    } 
       //......
}

最終我們步入createSentinelRedisInstance即可看到該方法通過與運算匹配出當前傳入的信息是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;
    //......
   //創建master實例
    ri = zmalloc(sizeof(*ri));
  //......
    ri->name = sdsname;
   //......

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

4. 啟動并監聽master

完成上述步驟后,redis得知當前節點是以哨兵模式啟動,于是調用sentinelIsRunning方法,內部遍歷masters節點的信息,發送到monitor頻道告知其他當前哨兵監聽的所有monitor信息

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

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

其內部調用sentinelGenerateInitialMonitorEvents遍歷masters表的信息將master發布到monitor頻道上:

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

查看sentinelGenerateInitialMonitorEvents邏輯就是遍歷masters表獲取master信息調用sentinelEvent向主節點master的monitor頻道上發布消息告知當前哨兵開始監控:

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

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

小結

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

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

2021-01-26 09:14:19

Linux內核模塊

2022-11-15 20:48:41

Linux

2023-11-12 23:08:17

C++初始化

2009-09-02 16:52:55

C#數組初始化

2011-03-16 10:52:20

2012-03-13 13:38:42

Java

2009-06-10 16:17:00

Netbeans JT初始化

2021-07-07 05:00:17

初始化源碼

2019-11-04 13:50:36

Java數組編程語言

2009-09-08 09:48:34

LINQ初始化數組

2009-11-11 15:29:15

ADO初始化

2010-07-28 10:22:33

FlexApplica

2021-03-12 10:30:11

SpringMVC流程初始化

2022-07-06 10:37:45

SpringServlet初始化

2020-12-03 09:50:52

容器IoC流程

2011-06-17 15:29:44

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

2020-11-23 14:22:17

代碼Go存儲

2012-05-23 12:46:53

JavaJava類

2010-09-08 14:49:09

藍牙協議棧

2023-10-06 20:57:52

C++聚合成員
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 这里只有精品99re | 看特级黄色片 | 亚洲精品久久久久久一区二区 | 亚洲毛片在线观看 | 国产男女猛烈无遮掩视频免费网站 | 成人精品毛片国产亚洲av十九禁 | 91精品一区二区三区久久久久久 | 国产九九九九 | 日韩精品一二三 | 国产黄色麻豆视频 | 91在线观看视频 | 国产一区二区三区在线免费观看 | 久久的色 | 欧美二三区 | 黄色片在线网站 | 欧美国产日韩精品 | 琪琪午夜伦伦电影福利片 | 中文字幕成人av | 日本精品视频 | 久久久123 | www.一级片 | 97精品超碰一区二区三区 | 罗宾被扒开腿做同人网站 | 欧美在线一区二区三区四区 | 亚洲欧美在线视频 | av网站免费观看 | 蜜桃一区二区三区 | 国产不卡视频 | 国产免费一区二区 | 精品一区二区三区视频在线观看 | 国产精品99久久久久久宅男 | 中文字幕在线观看一区二区 | 久久久精品一区 | 欧美黄色网| 高清欧美性猛交 | 久久综合国产 | 久久久久久久国产精品视频 | 国产在线一区观看 | 一级黄色片毛片 | 国产成人精品免费视频大全最热 | 黄色网址在线免费观看 |