redis集群实现(七)sentinel数据结构和初始

来源:互联网 发布:修改域名dns快速生效 编辑:程序博客网 时间:2024/06/05 17:49

上节我们看过了sentinel协调者模块处理集群故障节点的功能,今天我们看一下sentinel的处理架构以及sentinel的初始化流程。

首先,sentinel是独立于数据节点之外的一个协调模块,sentinel不存储任何用户要求存储的key-value数据,sentinel只负责监视集群中每一个节点的运行状态以及处理故障转移。

首先sentinel其实就是Redis进程的另一种模式,在Makefile里可以看出来,

[cpp] view plain copy
  1. # redis-server  
  2. $(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)                                                                                                      
  3.     $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)  
  4.   
  5. # redis-sentinel  
  6. $(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)  
  7.     $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)  

首先把所有的object编译,然后把所有的中间文件链接成为$(REDIS_SERVER_NAME),就是redis-server,然后安装的时候直接把redis-server安装成为redis-sentinel

我们首先看看sentinel的数据结构

[cpp] view plain copy
  1. /* sentinel主要状态结构体. */                                          
  2.  struct sentinelState {                                  
  3.      uint64_t current_epoch;     /* 当前的选举纪元,用于故障转移 */    
  4.      dict *masters;      /* sentinel正在监视的master实例哈希表,key是实例名称,value是sentinelRedisInstance指针 */  
  5.      int tilt;           /* tilt模式 */                  
  6.      int running_scripts;    /* 当前正在执行的脚本数目. */      
  7.      mstime_t tilt_start_time;   /* titl启动时间. */        
  8.      mstime_t previous_time;     /* 上一次执行时间处理函数. */                  
  9.      list *scripts_queue;    /* 脚本执行队列. */          
  10.      char *announce_ip;      /* 如果不是NULL,就代表通过gossip协议向此节点扩散. */     
  11.      int announce_port;      /* 如果不为0,就会被其他节点扩散 */   
  12.  } sentinel;  

然后我们再看看sentinelRedisInstance结构体,在sentinel中,每一个redis的主节点或者从节点,sentinel节点都会有一个此结构体表示

[cpp] view plain copy
  1. typedef struct sentinelRedisInstance {    
  2.      int flags;      /* 当前实例的类型 */                           
  3.      char *name;     /* 实例名称. */             
  4.      char *runid;    /* 本实例的运行时ID. */                        
  5.      uint64_t config_epoch;  /* 配置的初始选举纪元. */                            
  6.      sentinelAddr *addr; /* 实例的地址 */                                                                    
  7.  …………………………………………………                                           
  8.      /* 针对监视的master节点独有的. */                                        
  9.      dict *sentinels;    /* 其他正在监视这个master节点的sentinel. */                     
  10.      dict *slaves;       /* 此master的slave. */             
  11.      unsigned int quorum;/* 达成客观一致所需要的最小数目. */                              
  12. …………………………………………………                         
  13.      /* 针对监视的slave节点独有的. */                                       
  14. …………………………………………………                                
  15.      /* 故障转移处理涉及到的变量 */                                          
  16. …………………………………………………                                                      
  17.  } sentinelRedisInstance;  

我们继续看sentinel初始化相关流程。

在启动sentinel的时候我们只需要redis-sentinel sentinel.conf就可以启动一个sentinel实例,配置文件中有redis集群中的master节点的IP,端口,名称以及处理故障转移的时间限制等参数。

main函数里边首先会检查是什么模式启动的,如果是sentinel模式启动的,就记录下来。

[cpp] view plain copy
  1. int main(int argc, char **argv) {  
  2.     struct timeval tv;  
  3. …………………………………………………      
  4.     // 检查服务器是否以 Sentinel 模式启动         
  5.     server.sentinel_mode = checkForSentinelMode(argc,argv);  
  6.   
  7.     // 初始化服务器  
  8.     initServerConfig();  
  9.   
  10.     /* We need to init sentinel right now as parsing the configuration file 
  11.      * in sentinel mode will have the effect of populating the sentinel 
  12.      * data structures with master nodes to monitor. */  
  13.     // 如果服务器以 Sentinel 模式启动,那么进行 Sentinel 功能相关的初始化  
  14.     // 并为要监视的主服务器创建一些相应的数据结构  
  15.     if (server.sentinel_mode) {  
  16.         initSentinelConfig();              
  17.         initSentinel();  
  18. …………………………………………………        
  19.     //初始化配置文件  
  20.          if (configfile) server.configfile = getAbsolutePath(configfile);                             
  21.          resetServerSaveParams();                              
  22.          loadServerConfig(configfile,options);                              
  23.          sdsfree(options);  
  24. …………………………………………………        
  25.     }  
  26. …………………………………………………        

可以看出,如果是以sentinel模式启动的话,会执行initSentinelConfiginitSentinel函数,首先是initSentinelConfig函数初始化sentinel的配置,最后执行loadServerConfig函数来解析配置文件

[cpp] view plain copy
  1. void initSentinelConfig(void) {              
  2.     server.port = REDIS_SENTINEL_PORT;  
  3. }  

仅仅是配置下sentinel的专属端口,我们继续看下initSentinel函数。

[cpp] view plain copy
  1. // 在sentinel 模式下初始化  
  2. void initSentinel(void) {  
  3.     int j;  
  4.   
  5.     // 清空服务器命令哈希表  
  6.     dictEmpty(server.commands,NULL);  
  7.     // 填充sentinel的专属命令  
  8.     for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {  
  9.         int retval;  
  10.         struct redisCommand *cmd = sentinelcmds+j;  
  11.   
  12.         retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);  
  13.         redisAssert(retval == DICT_OK);  
  14.     }  
  15.   
  16.     //初始化 Sentinel 的状态,比如选举纪元以及保存master信息的字典  
  17.     sentinel.current_epoch = 0;  
  18.   
  19.     sentinel.masters = dictCreate(&instancesDictType,NULL);  
  20.   
  21.     // 初始化tilt相关  
  22.     sentinel.tilt = 0;  
  23.     sentinel.tilt_start_time = 0;  
  24.     sentinel.previous_time = mstime();  
  25.   
  26.     // 初始化运行脚本的变量  
  27.     sentinel.running_scripts = 0;  
  28.     sentinel.scripts_queue = listCreate();  
  29. }   

initSentinel函数初始化了命令集以及处理故障转移相关的变量,sentinel专属的命令集喝如下:

[cpp] view plain copy
  1. // 服务器在 sentinel 模式下可执行的命令  
  2. struct redisCommand sentinelcmds[] = {                                                                                                         
  3.     {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},  
  4.     {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0},  
  5.     {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0},  
  6.     {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},  
  7.     {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0},  
  8.     {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},  
  9.     {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0},  
  10.     {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0},  
  11.     {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}  
  12. };  

最后看看loadServerConfig函数,

[cpp] view plain copy
  1. void loadServerConfig(char *filename, char *options) {                                                                                                         
  2.     sds config = sdsempty();                                                                                                                                   
  3.     char buf[REDIS_CONFIGLINE_MAX+1];                                                                                                                          
  4.                                                                                                                                                                
  5.     /* 载入文件内容 */                                                                                                                                
  6.     if (filename) {                                                                                                                                            
  7.         FILE *fp;                                                                                                                                              
  8.                                                                                                                                                                
  9.         if (filename[0] == '-' && filename[1] == '\0') {                                                                                                       
  10.             fp = stdin;                                                                                                                                        
  11.         } else {                                                                                                                                               
  12.             if ((fp = fopen(filename,"r")) == NULL) {                                                                                                                         redisLog(REDIS_WARNING,                                                                                                                        
  13.                     "Fatal error, can't open config file '%s'", filename);                                                                                     
  14.                 exit(1);                                                                                                                                       
  15.             }                                                                                                                                                  
  16.         }                                                                                                                                                      
  17.         while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)                                                                                                    
  18.             config = sdscat(config,buf);                                                                                                                       
  19.         if (fp != stdin) fclose(fp);                                                                                                                           
  20.     }                                                                                                                                                          
  21.     /* Append the additional options */                                                                                                                        
  22.     if (options) {                                                                                                                                             
  23.         config = sdscat(config,"\n");                                                                                                                          
  24.         config = sdscat(config,options);                                                                                                                       
  25.     }                                    
  26.     //读取完毕,解析配置                                                                                                                        
  27.     loadServerConfigFromString(config);                                                                                                                        
  28.     sdsfree(config);                                                                                                                                           
  29. }   

然后我们进入loadServerConfigFromString函数,

[cpp] view plain copy
  1. void loadServerConfigFromString(char *config) {                             
  2.      char *err = NULL;                               
  3.      int linenum = 0, totlines, i;                               
  4.      int slaveof_linenum = 0;                               
  5.      sds *lines;    
  6.   
  7.      for (i = 0; i < totlines; i++) {        
  8. …………………………………………………        
  9.           } else if (!strcasecmp(argv[0],"sentinel")) {                              
  10.              /* sentinel启动必须有一个config文件*/    
  11.              if (argc != 1) {                                      
  12.                  if (!server.sentinel_mode) {                               
  13.                      err = "sentinel directive while not in sentinel mode";                                   
  14.                      goto loaderr;                              
  15.                  }                              
  16.         //解析配置文件的主函数                  
  17.                  err = sentinelHandleConfiguration(argv+1,argc-1);                             
  18.                  if (err) goto loaderr;                              
  19.              }                                
  20.          }  
  21.      }  
  22. …………………………………………………        

继续看sentinelHandleConfiguration函数

[cpp] view plain copy
  1. /* ============================ Config handling ============================= */                                                                               
  2.  char *sentinelHandleConfiguration(char **argv, int argc) {                                                                                                     
  3.      sentinelRedisInstance *ri;                                                                                                                                 
  4.      //根据配置文件,创建sentinel实例  
  5.      if (!strcasecmp(argv[0],"monitor") && argc == 5) {                                                                                                         
  6.          /* monitor <name> <host> <port> <quorum> */                                                                                                            
  7.          int quorum = atoi(argv[4]);                                                                                                                            
  8.                                                                                                                                                                 
  9.          if (quorum <= 0) return "Quorum must be 1 or greater.";                                                                                                
  10.          if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2],                                                                                            
  11.                                          atoi(argv[3]),quorum,NULL) == NULL)                                                                                    
  12.          {                                                                                                                                                      
  13.              switch(errno) {                                                                                                                                    
  14.              case EBUSY: return "Duplicated master name.";                                                                                                      
  15.              case ENOENT: return "Can't resolve master instance hostname.";                                                                                     
  16.              case EINVAL: return "Invalid port number";                                                                                                         
  17.              }                                                                                                                                                  
  18.          }                                                                                    
  19.      //解析其他的sentinel故障转移相关的参数           
  20. …………………………………………………                                                                                                                                                                                                          
  21.      } else {                                                                                                                                                   
  22.          return "Unrecognized sentinel configuration statement.";                                                                                               
  23.      }                                                                                                                                                          
  24.      return NULL;                                                                                                                                               
  25.  }   

自此,sentinel初始化完毕,然后第一个周期会和redis节点进行连接,接下来就等待sentinel的定时器周期的进行状态检查以及故障转移功能了。

原创粉丝点击