redis集群实现(七)sentinel数据结构和初始
来源:互联网 发布:激战2战场实时数据 编辑:程序博客网 时间:2024/05/29 18:00
上节我们看过了sentinel协调者模块处理集群故障节点的功能,今天我们看一下sentinel的处理架构以及sentinel的初始化流程。
首先,sentinel是独立于数据节点之外的一个协调模块,sentinel不存储任何用户要求存储的key-value数据,sentinel只负责监视集群中每一个节点的运行状态以及处理故障转移。
首先sentinel其实就是redis进程的另一种模式,在Makefile里可以看出来,
# redis-server$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)# redis-sentinel$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)
首先把所有的object编译,然后把所有的中间文件链接成为$(REDIS_SERVER_NAME),就是redis-server,然后安装的时候直接把redis-server安装成为redis-sentinel。
我们首先看看sentinel的数据结构
/* sentinel主要状态结构体. */ struct sentinelState { uint64_t current_epoch; /* 当前的选举纪元,用于故障转移 */ dict *masters; /* sentinel正在监视的master实例哈希表,key是实例名称,value是sentinelRedisInstance指针 */ int tilt; /* tilt模式 */ int running_scripts; /* 当前正在执行的脚本数目. */ mstime_t tilt_start_time; /* titl启动时间. */ mstime_t previous_time; /* 上一次执行时间处理函数. */ list *scripts_queue; /* 脚本执行队列. */ char *announce_ip; /* 如果不是NULL,就代表通过gossip协议向此节点扩散. */ int announce_port; /* 如果不为0,就会被其他节点扩散 */ } sentinel;
然后我们再看看sentinelRedisInstance结构体,在sentinel中,每一个redis的主节点或者从节点,sentinel节点都会有一个此结构体表示
typedef struct sentinelRedisInstance { int flags; /* 当前实例的类型 */ char *name; /* 实例名称. */ char *runid; /* 本实例的运行时ID. */ uint64_t config_epoch; /* 配置的初始选举纪元. */ sentinelAddr *addr; /* 实例的地址 */ ………………………………………………… /* 针对监视的master节点独有的. */ dict *sentinels; /* 其他正在监视这个master节点的sentinel. */ dict *slaves; /* 此master的slave. */ unsigned int quorum;/* 达成客观一致所需要的最小数目. */ ………………………………………………… /* 针对监视的slave节点独有的. */ ………………………………………………… /* 故障转移处理涉及到的变量 */ ………………………………………………… } sentinelRedisInstance;
我们继续看sentinel初始化相关流程。
在启动sentinel的时候我们只需要redis-sentinel sentinel.conf就可以启动一个sentinel实例,配置文件中有redis集群中的master节点的IP,端口,名称以及处理故障转移的时间限制等参数。
在main函数里边首先会检查是什么模式启动的,如果是sentinel模式启动的,就记录下来。
int main(int argc, char **argv) { struct timeval tv;………………………………………………… // 检查服务器是否以 Sentinel 模式启动 server.sentinel_mode = checkForSentinelMode(argc,argv); // 初始化服务器 initServerConfig(); /* We need to init sentinel right now as parsing the configuration file * in sentinel mode will have the effect of populating the sentinel * data structures with master nodes to monitor. */ // 如果服务器以 Sentinel 模式启动,那么进行 Sentinel 功能相关的初始化 // 并为要监视的主服务器创建一些相应的数据结构 if (server.sentinel_mode) { initSentinelConfig(); initSentinel();………………………………………………… //初始化配置文件 if (configfile) server.configfile = getAbsolutePath(configfile); resetServerSaveParams(); loadServerConfig(configfile,options); sdsfree(options);………………………………………………… }…………………………………………………
可以看出,如果是以sentinel模式启动的话,会执行initSentinelConfig和initSentinel函数,首先是initSentinelConfig函数初始化sentinel的配置,最后执行loadServerConfig函数来解析配置文件。
void initSentinelConfig(void) { server.port = REDIS_SENTINEL_PORT;}
仅仅是配置下sentinel的专属端口,我们继续看下initSentinel函数。
// 在sentinel 模式下初始化void initSentinel(void) { int j; // 清空服务器命令哈希表 dictEmpty(server.commands,NULL); // 填充sentinel的专属命令 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 的状态,比如选举纪元以及保存master信息的字典 sentinel.current_epoch = 0; sentinel.masters = dictCreate(&instancesDictType,NULL); // 初始化tilt相关 sentinel.tilt = 0; sentinel.tilt_start_time = 0; sentinel.previous_time = mstime(); // 初始化运行脚本的变量 sentinel.running_scripts = 0; sentinel.scripts_queue = listCreate();}
initSentinel函数初始化了命令集以及处理故障转移相关的变量,sentinel专属的命令集喝如下:
// 服务器在 sentinel 模式下可执行的命令struct redisCommand sentinelcmds[] = { {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0}, {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0}, {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0}, {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0}, {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0}, {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0}, {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}};
最后看看loadServerConfig函数,
void loadServerConfig(char *filename, char *options) { sds config = sdsempty(); char buf[REDIS_CONFIGLINE_MAX+1]; /* 载入文件内容 */ if (filename) { FILE *fp; if (filename[0] == '-' && filename[1] == '\0') { fp = stdin; } else { if ((fp = fopen(filename,"r")) == NULL) { redisLog(REDIS_WARNING, "Fatal error, can't open config file '%s'", filename); exit(1); } } while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) config = sdscat(config,buf); if (fp != stdin) fclose(fp); } /* Append the additional options */ if (options) { config = sdscat(config,"\n"); config = sdscat(config,options); } //读取完毕,解析配置 loadServerConfigFromString(config); sdsfree(config); }
然后我们进入loadServerConfigFromString函数,
void loadServerConfigFromString(char *config) { char *err = NULL; int linenum = 0, totlines, i; int slaveof_linenum = 0; sds *lines; for (i = 0; i < totlines; i++) { ………………………………………………… } else if (!strcasecmp(argv[0],"sentinel")) { /* sentinel启动必须有一个config文件*/ if (argc != 1) { if (!server.sentinel_mode) { err = "sentinel directive while not in sentinel mode"; goto loaderr; } //解析配置文件的主函数 err = sentinelHandleConfiguration(argv+1,argc-1); if (err) goto loaderr; } } }…………………………………………………
继续看sentinelHandleConfiguration函数
/* ============================ Config handling ============================= */ char *sentinelHandleConfiguration(char **argv, int argc) { sentinelRedisInstance *ri; //根据配置文件,创建sentinel实例 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."; if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2], atoi(argv[3]),quorum,NULL) == NULL) { switch(errno) { case EBUSY: return "Duplicated master name."; case ENOENT: return "Can't resolve master instance hostname."; case EINVAL: return "Invalid port number"; } } //解析其他的sentinel故障转移相关的参数 ………………………………………………… } else { return "Unrecognized sentinel configuration statement."; } return NULL; }
自此,sentinel初始化完毕,然后第一个周期会和redis节点进行连接,接下来就等待sentinel的定时器周期的进行状态检查以及故障转移功能了。
- redis集群实现(七)sentinel数据结构和初始
- redis集群实现(七)sentinel数据结构和初始
- redis集群和Sentinel功能
- Redis Sentinel实现集群节点故障转移
- redis集群部署sentinel--两台实现
- 借助sentinel 实现redis集群HA配置
- Redis: sentinel集群的搭建和测试
- Redis sentinel集群实验
- Redis Sentinel集群方案
- redis+sentinel集群部署
- redis-sentinel集群安装
- Redis主从集群以及sentinel集群安装和配置总结
- spring data redis 集群(sentinel实现)和simple spring memcached分布式初使用
- redis集群实现(五) sentinel的架构与raft协议
- CentOS 7.3 Sentinel实现Redis集群高可用部署
- redis集群部署sentinel—两台服务器实现
- 基于Sentinel(哨兵)搭建实现Redis高可用集群
- Redis多机数据库的实现(集群、复制、sentinel)
- 懂iOS平台的OpenGL ES请进
- 写在博客访问量超过90万之际
- [leetcode]441. Arranging Coins
- 综合作业——APP(2)
- 数据可视化matplotlib的应用(二)
- redis集群实现(七)sentinel数据结构和初始
- 新手,eclipse 连接android sdk出现的问题,怎么解决如下图
- 文本、图片无法一起显示,菜鸟求助!!!
- asp程序调用SQL Server的存储过程,获取不到输出参数的值
- 如何在Eclipse中 NDK r-10实现单步调试jni代码
- app通过自己的服务器怎么更新?可行吗?
- android4.0自定义锁屏apk形式总结
- 41. First Missing Positive
- Android中Html的显示与其中图片的异步加载