分布式缓存系统TAIR代码分析一

本文详细剖析了Tair存储系统的架构与实现原理,包括代码结构、配置服务器的工作流程及其实现细节,如主备切换机制、数据服务器状态检查等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tair 是分布式、高性能、可扩展、搞可靠性的存储系统。目前,Tair已成为开源项目,代码在:http://code.taobao.org/p/tair/src/. 关于Tair的更多介绍,见http://code.taobao.org/p/tair/wiki/index/


几个月以前,我就有计划写一个TAIR代码分析文档。由于个人精力和时间的限制,迟迟未动笔。在13年伊始,我真心觉得有必要对整个系统代码作一个深入的了解。我的突破口是从客户端开始入手。当线上遇到问题时候,希望能够快速定位问题原因,并排除之。于是乎,我正式动工了。闲话不说了。

1. 代码结构

首先,简要介绍Tair的代码结构,如下所示:

[plain]  view plain  copy
  1. .  
  2. |-- AUTHORS  
  3. |-- COPYING  
  4. |-- ChangeLog  
  5. |-- Makefile.am  
  6. |-- NEWS  
  7. |-- README  
  8. |-- bootstrap.sh  
  9. |-- configure.ac  
  10. |-- contrib  
  11. |-- doc  
  12. |-- packages  
  13. |-- rpm   
  14. |-- scripts  
  15. |-- share  
  16. |-- src   
  17. |   |-- Makefile.am  
  18. |   |-- client  
  19. |   |-- common  
  20. |   |-- configserver  
  21. |   |-- dataserver  
  22. |   |-- invalserver  
  23. |   |-- packets  
  24. |   |-- plugin  
  25. |   |-- statserver  
  26. |   |   |-- Makefile.am  
  27. |   |   |-- include  
  28. |   |   `-- storage  
  29. |   |-- storage  
  30. |   |   |-- fdb  
  31. |   |   |-- kdb  
  32. |   |   |-- ldb  
  33. |   |   |-- mdb  
  34. |   |   `-- storage_manager.hpp  
  35. |   `-- tools  
  36. |-- tcmalloc.m4  
  37.  `-- test  
整个系统的主要代码集中在src目录下。在该目录下,client目录中包含了c++客户端的所有代码,应用通过该客户端访问Tair系统;commmon目录中包含了一些工具函数或者共用的C++类的实现;configserver中包含了配置服务实现的代码;dataserver中包含了数据服务器的代码实现;invalserver目录中包含了失效服务器的实现代码;storage目录中包含了Tair底层所使用的存储引擎, 目前Tair主要使用2中存储引擎——mdb,leveldb; tools目录中包含了部分系统运维工具;test目录中包含了测试文件。

Tair系统还依赖底层网络编程库——tbnet,tbsys. 这两个底层库提供了网络通信,配置文件管理,等基础功能;这里就不一一介绍了。

2. 配置服务器

2.1 main

配置服务器(下文称为configserver)是tair系统的中心节点。该节点提供全局信息管理功能,通过主备机制实现中心节点的高可用性。在Tair系统中,configserver提供了哪些功能呢?configserver 具有对对照表(server table)的管理,维护主备配置服务器之间的心跳,维护配置服务器与数据服务器之间的心跳,对数据迁移的管理,以及其他的全局信息管理。下文以这些功能为线索,进行代码分析。

接下来就先分析该服务器的main函数吧,代码如下:

[cpp]  view plain  copy
  1. main(int argc, char *argv[])  
  2. {  
  3.   char *config_file = parse_cmd_line(argc, argv);  
  4.   
  5.   if(TBSYS_CONFIG.load(config_file)) {  
  6.     return EXIT_FAILURE;  
  7.   }  
  8.     
  9.   if(!tbsys::CFileUtil::mkdirs(dir_path)) {  
  10.    return EXIT_FAILURE;  
  11.  }     
  12.  if(tbsys::CProcess::startDaemon(sz_pid_file, sz_log_file) == 0) {  
  13.    
  14.    tair_cfg_svr = new tair::config_server::tair_config_server();  
  15.    tair_cfg_svr->start();       
  16.    delete tair_cfg_svr;  
  17.  }   
  18. return EXIT_SUCCESS;  
  19. }   

为了方便分析,上述代码经过精简的。可以看出,main函数在加载配置文件内容到内存,创建工作目录,将本线程设置为后台线程。接着,创建tair::config_server对象,调用start函数,configserver就在完成初始化后,就开始对外提供服务了。

2.2 configserver初始化

configserver的初始化工作主要有start中完成的。该函数首先启动线程池,该线程池用于处理各种请求;打开2个TCP端口,一个用于处理对外提供服务,另外一个用于主备configserver之间的心跳;再开一个称为my_server_conf_thread的线程。代码如下:

[cpp]  view plain  copy
  1. void tair_config_server::start()  
  2. {  
  3.   if(initialize()) {  
  4.     return;  
  5.   }  
  6.   
  7.   task_queue_thread.start();  
  8.   
  9.   if(heartbeat_transport.listen(spec, &packet_streamer, this) == NULL) {  
  10.   }  
  11.   
  12.   if(packet_transport.listen(spec, &packet_streamer, this) == NULL) {  
  13.   }  
  14.   
  15.   packet_transport.start();  
  16.   heartbeat_transport.start();  
  17.   my_server_conf_thread.start();  
  18.   
  19.   task_queue_thread.wait();  
  20.   my_server_conf_thread.wait();  
  21.   packet_transport.wait();  
  22.   heartbeat_transport.wait();  
  23.   
  24.   destroy();  
  25. }  

task_queue_thread.start, 在没有请求请求来的情况下,就会等待;packet_transport, heartbeat_transport.start 后,将接受到的请求,放入到线程池队列中,工作线程处理请求。这里主要分析my_server_conf_thread线程。该线程的run函数做了如下工作:

1) 读取配置文件,建立保存group信息的数据结构,这些信息包括该group的数据服务器列表等;

2) 如果主配置服务器存在并且alive, 备配置服务器从主配置服务器上读取group的配置信息,而不是从配置文件中读取;

3)建立循环,循环做如下事情:

       a) 检查group的配置文件版本信息,确定是否要重新读取配置文件;

       b) 检查group的对照表,确定是否要重新建立对照表,若需重建,设置重建标志;

       c) 检查配置服务器的状态;

       d) 检查数group的数据服务器的状态;

贴上该线程的run函数的主要代码,代码中添加了注释,以便理解:

[cpp]  view plain  copy
  1. void server_conf_thread::run(tbsys::CThread * threadvoid *arg)  
  2. {  
  3.   //根据配置文件中配置的主备CS的IP和本机的IP,来确定当前cs是作为主配置服务器,还是作为备配置服务器;  
  4.   uint64_t sync_config_server_id = 0;  
  5.   uint64_t tmp_master_config_server_id = master_config_server_id;  
  6.   if(tmp_master_config_server_id == util::local_server_ip::ip) {  
  7.     tmp_master_config_server_id = get_slave_server_id();  
  8.   }  
  9.   if(tmp_master_config_server_id)  
  10.   //作为备配置服务器在运行  
  11.   sync_config_server_id =  
  12.     get_master_config_server(tmp_master_config_server_id, 1);   
  13.   if(sync_config_server_id) {  
  14.     log_info("syncConfigServer: %s",  
  15.         tbsys::CNetUtil::addrToString(sync_config_server_id).  
  16.         c_str());  
  17.   }  
  18.   
  19.   // 读取group的配置文件名称,这里需要注意的是,在同一个Group中可以配置多个group;  
  20.   const char *group_file_name =  
  21.     TBSYS_CONFIG.getString(CONFSERVER_SECTION, TAIR_GROUP_FILE, NULL);  
  22.   if(group_file_name == NULL) {  
  23.     log_error("not found %s:%s ", CONFSERVER_SECTION, TAIR_GROUP_FILE);  
  24.     return;  
  25.   }  
  26.   //将group的配置文件的最后修改时间作为该文件的版本号;  
  27.   uint32_t config_version_from_file = get_file_time(group_file_name);  
  28.   //bool rebuild_group = true;  
  29.   bool rebuild_this_group = false;  
  30.   // 这里读取配置文件。对于备配置服务器而言,它需要从主配置服务器中读取配置文件,如果主配置服务器是alive的话;  
  31.   load_group_file(group_file_name, config_version_from_file,  
  32.       sync_config_server_id);  
  33.   //if (syncConfigServer != 0) {  
  34.   //    rebuild_group = false;  
  35.   //}   
  36.   //当当前机器运行的cs是主配置服务器,或者主配置服务器down掉时,运行以下代码  
  37.   //set myself is master  
  38.   if(config_server_info_list.size() == 2U &&  
  39.       (config_server_info_list[0]->server_id == util::local_server_ip::ip  
  40.        || (sync_config_server_id == 0  
  41.          && config_server_info_list[1]->server_id ==  
  42.          util::local_server_ip::ip))) {  
  43.     //running as a master, or not find the master , only the slave.  
  44.     master_config_server_id = util::local_server_ip::ip;  
  45.     log_info("set MASTER_CONFIG: %s",  
  46.         tbsys::CNetUtil::addrToString(master_config_server_id).  
  47.         c_str());  
  48.     {  
  49.       //group info map数据结构在读取配置文件时候建立,具体在load_group_file中完成  
  50.       group_info_map::iterator it;  
  51.       group_info_rw_locker.rdlock();  
  52.       for(it = group_info_map_data.begin();  
  53.           it != group_info_map_data.end(); ++it) {  
  54.         if(rebuild_this_group) {  
  55.           it->second->set_force_rebuild();  
  56.         }  
  57.         else {  
  58.           //通过检查对照表中是否存在0的项,来确定是否需要重建对照表。对照表的建立是以group为单位的,各个group维护自己的对照表;  
  59.           bool need_build_it = false;  
  60.           const uint64_t *p_table = it->second->get_hash_table();  
  61.           log_debug("server_bucket_count: %d", it->second->get_server_bucket_count());  
  62.           for(uint32_t i = 0; i < it->second->get_server_bucket_count();  
  63.               ++i) {  
  64.             if(p_table[i] == 0) {  
  65.               need_build_it = true;  
  66.               break;  
  67.             }  
  68.           }  
  69.           if(need_build_it) {  
  70.             //这里将标记置为1,表示该Group的对照表需要重建  
  71.             it->second->set_force_rebuild();  
  72.           }  
  73.           else {  
  74.             it->second->set_table_builded();  
  75.           }  
  76.         }  
  77.       }  
  78.       group_info_rw_locker.unlock();  
  79.     }  
  80.   }  
  81.   
  82.   // will start loop  
  83.   uint32_t loop_count = 0;  
  84.   tzset();  
  85.   int log_rotate_time = (time(NULL) - timezone) % 86400;  
  86.   struct timespec tm;  
  87.   clock_gettime(CLOCK_MONOTONIC, &tm);  
  88.   //记录心跳时间  
  89.   heartbeat_curr_time = tm.tv_sec;  
  90.   is_ready = true;  
  91.   //开始loop  
  92.   while(!_stop) {  
  93.     struct timespec tm;  
  94.     clock_gettime(CLOCK_MONOTONIC, &tm);  
  95.     heartbeat_curr_time = tm.tv_sec;  
  96.     //检查另外一个cs是否down掉  
  97.     for(size_t i = 0; i < config_server_info_list.size(); i++) {  
  98.       server_info *server_info_it = config_server_info_list[i];  
  99.       if(server_info_it->server_id == util::local_server_ip::ip)  
  100.         continue;  
  101.       if(server_info_it->status != server_info::ALIVE  
  102.           && (loop_count % 30) != 0) {  
  103.         down_slave_config_server = server_info_it->server_id;  
  104.         log_debug("local server: %s set down slave config server: %s",  
  105.             tbsys::CNetUtil::addrToString(util::local_server_ip::ip).c_str(),  
  106.             tbsys::CNetUtil::addrToString(down_slave_config_server).c_str());  
  107.         continue;  
  108.       }  
  109.       //向另外一个cs发送心跳包  
  110.       down_slave_config_server = 0;  
  111.       request_conf_heartbeart *new_packet = new request_conf_heartbeart();  
  112.       new_packet->server_id = util::local_server_ip::ip;  
  113.       new_packet->loop_count = loop_count;  
  114.       if(connmgr_heartbeat->  
  115.           sendPacket(server_info_it->server_id, new_packet) == false) {  
  116.         delete new_packet;  
  117.       }  
  118.     }  
  119.   
  120.     loop_count++;  
  121.     if(loop_count <= 0)  
  122.       loop_count = TAIR_SERVER_DOWNTIME + 1;  
  123.     //读取配置文件的版本信息,检查是否需要重新读取配置文件,每次修改配置文件,总会触发CS重新读取配置文件  
  124.     uint32_t curver = get_file_time(group_file_name);  
  125.     if(curver > config_version_from_file) {  
  126.       log_info("groupFile: %s, curver:%d configVersion:%d",  
  127.           group_file_name, curver, config_version_from_file);  
  128.       //read config from file  
  129.       //读取配置文件,第3个参数为0,表示此次只从配置文件读取,不从另外一个CS上读取配置文件  
  130.       load_group_file(group_file_name, curver, 0);  
  131.       config_version_from_file = curver;  
  132.       //作为主CS,将新的配置文件发送到备CS  
  133.       send_group_file_packet();  
  134.     }  
  135.     //检查配置服务器状态  
  136.     check_config_server_status(loop_count);  
  137.     //检查数据服务器状态  
  138.     if(loop_count > TAIR_SERVER_DOWNTIME) {  
  139.       check_server_status(loop_count);  
  140.     }  
  141.   
  142.     // 日志回滚  
  143.     log_rotate_time++;  
  144.     if(log_rotate_time % 86400 == 86340) {  
  145.       log_info("rotateLog End");  
  146.       TBSYS_LOGGER.rotateLog(NULL, "%d");  
  147.       log_info("rotateLog start");  
  148.     }  
  149.     if(log_rotate_time % 3600 == 3000) {  
  150.       log_rotate_time = (time(NULL) - timezone) % 86400;  
  151.     }  
  152.     //休息1s  
  153.     TAIR_SLEEP(_stop, 1);  
  154.   }  
  155.   is_ready = false;  
  156. }  

接下来分析load_group_file函数,该函数主要功能是获取配置文件,当配置文件版本号发生变化后,就会调用该函数,获取最新的配置信息;上代码,在代码中添加注释:

[cpp]  view plain  copy
  1. void server_conf_thread::load_group_file(const char *file_name,  
  2.                                              uint32_t version,  
  3.                                              uint64_t sync_config_server_id)  
  4. {  
  5.   if(file_name == NULL)  
  6.     return;  
  7.   tbsys::CConfig config;  
  8.   if(config.load((char *) file_name) == EXIT_FAILURE) {  
  9.     log_error("load config file %s error", file_name);  
  10.     return;  
  11.   }  
  12.   
  13.   log_info("begin load group file, filename: %s, version: %d", file_name,  
  14.       version);  
  15.   //这里获取所有group的名称  
  16.   vector<string> section_list;  
  17.   config.getSectionName(section_list);  
  18.   // start up, get hash table from an other config server if exist one.  
  19.   //若sync_config_server_id不为0, 则表示从sync_config_server_id对应的CS上获取配置信息  
  20.   //若从另外一个CS上配置服务器上获取到了GROUP的配置信息后,就不再读取本地配置文件了  
  21.   if(sync_config_server_id  
  22.       && sync_config_server_id != util::local_server_ip::ip) {  
  23.     get_server_table(sync_config_server_id, NULL, GROUP_CONF);  
  24.     for(size_t i = 0; i < section_list.size(); i++) {  
  25.       get_server_table(sync_config_server_id,  
  26.           (char *) section_list[i].c_str(), GROUP_DATA);  
  27.     }     
  28.     // here we need reload group configfile, since the file has been synced  
  29.     if(config.load((char *) file_name) == EXIT_FAILURE) {  
  30.       log_error("reload config file error: %s", file_name);  
  31.       return;  
  32.     }     
  33.   }  
  34.   
  35.   set<uint64_t> server_id_list;  
  36.   group_info_rw_locker.wrlock();  
  37.   server_info_rw_locker.wrlock();  
  38.   for(size_t i = 0; i < section_list.size(); i++) {  
  39.     group_info_map::iterator it =  
  40.       group_info_map_data.find(section_list[i].c_str());  
  41.     group_info *group_info_tmp = NULL;  
  42.     if(it == group_info_map_data.end()) {  
  43.       //根据每一个group name来创建group_info数据结构,该数据结构包含了一个group所需的  
  44.       group_info_tmp =  
  45.         new group_info((char *) section_list[i].c_str(),  
  46.             &data_server_info_map, connmgr);  
  47.       group_info_map_data[group_info_tmp->get_group_name()] =  
  48.         group_info_tmp;  
  49.     }  
  50.     else {  
  51.       grjup_info_tmp = it->second;  
  52.     }  
  53.     //group加载自身配置信息  
  54.     group_info_tmp->load_config(config, version, server_id_list);  
  55.     //获取有效的数据服务器  
  56.     group_info_tmp->find_available_server();  
  57.   }  
  58.   //由于配置文件变动,可能导致数据服务器列表中的内容也发生变化。若发送变化,就需要去掉过时的数据服务器信息  
  59.   set <uint64_t> map_id_list;  
  60.   set <uint64_t> should_del_id_list;  
  61.   server_info_map::iterator sit;  
  62.   for(sit = data_server_info_map.begin();  
  63.       sit != data_server_info_map.end(); ++sit) {  
  64.     map_id_list.insert(sit->second->server_id);  
  65.   }  
  66.   std::set_difference(map_id_list.begin(), map_id_list.end(),  
  67.       server_id_list.begin(), server_id_list.end(),  
  68.       inserter(should_del_id_list,  
  69.         should_del_id_list.begin()));  
  70.   for(set<uint64_t>::iterator it = should_del_id_list.begin();  
  71.       it != should_del_id_list.end(); ++it) {  
  72.     sit = data_server_info_map.find(*it);  
  73.     if(sit != data_server_info_map.end()) {  
  74.       sit->second->server_id = 0;  
  75.       data_server_info_map.erase(sit);  
  76.     }  
  77.   }  
  78.   for(group_info_map::iterator it = group_info_map_data.begin();  
  79.       it != group_info_map_data.end(); ++it) {  
  80.     it->second->correct_server_info(sync_config_server_id  
  81.         && sync_config_server_id !=  
  82.         util::local_server_ip::ip);  
  83.   }  
  84.   log_info("machine count: %d, group count: %d",  
  85.       data_server_info_map.size(), group_info_map_data.size());  
  86.   server_info_rw_locker.unlock();  
  87.   group_info_rw_locker.unlock();  
  88. }  
在my_server_conf_thread的run函数中,若作为master运行的时候,检测到配置文件发生变化后,调用load_group_file来读取最新配置文件。在获取新的配置内容后,调用 send_group_file_packet函数将新的配置文件发送到slave上。因此在修改一个集群的配置文件时候,只需修改master的配置文件,修改后的内容会自动同步到slave服务器上。

2.3 配置服务器状态检查

在my_server_conf_thread的run函数中,周期性检查配置服务器和数据服务器的状态,若状态发生变化,则做相应处理。若作为主配置服务器运行,该函数检查备配置服务器的状态,能检查到2种状态变化:

1) 备配置服务器down掉,不需发送主备配置服务器切换;

 2)   备配置服务器up了,此时也不需要发生主备配置服务器切换;在这种情况下, 备份配置服务器需要和主配置服务同步数据;

若作为备配置服务器运行,则该函数将检查主配置服务器的状态,能检查到2种状态变化:

1)主配置服务器down了,发生主备配置服务器切换;

2)主配置服务器up,也发生主备配置服务器切换;若发生切换,作为主配置服务器运行的代码,会重新读取配配置文件;

本节分析,检查配置服务器状态的代码。上代码如下:

[cpp]  view plain  copy
  1. void server_conf_thread::check_config_server_status(uint32_t loop_count)  
  2. {  
  3.   //若只有一个配置服务器,则该函数无需做更多功能  
  4.   if(config_server_info_list.size() != 2U) {  
  5.     return;  
  6.   }  
  7.   bool master_status_changed = false;  
  8.   //每一个配置服务器记录其上一次收到对方心跳包的时机,根据这个时间,和一个全局的心跳时间,来检测另外一个配置服务器的状态  
  9.   uint32_t now = heartbeat_curr_time - TAIR_SERVER_DOWNTIME * 2;  
  10.   bool config_server_up = false;  
  11.   for(size_t i = 0; i < config_server_info_list.size(); i++) {  
  12.     server_info *server_info_tmp = config_server_info_list[i];  
  13.     //只检查另外一个配置服务器  
  14.     if(server_info_tmp->server_id == util::local_server_ip::ip) {        // this is myself  
  15.       continue;  
  16.     }     
  17.     //如果超过TAIR_SERVER_DOWNTIME * 2时间内没有收到另外一个配置服务器的心跳包,该配置服务器如果其记录状态为alive,再次通过发送一个请求包到对端服务器,若对端服务器没有返回,则认为对端的配置服务器已经down了;  
  18.     if(server_info_tmp->last_time < now && server_info_tmp->status == server_info::ALIVE) {        // downhost  
  19.       if(tbnet::ConnectionManager::isAlive(server_info_tmp->server_id) ==  
  20.           true)  
  21.         continue;  
  22.       server_info_tmp->status = server_info::DOWN;  
  23.       //若down掉的配置服务器,在配置文件中配置的为主配置服务器,则表示需要切换主配置服务器了  
  24.       if(i == 0)  
  25.         master_status_changed = true;  
  26.       log_warn("CONFIG HOST DOWN: %s",  
  27.           tbsys::CNetUtil::addrToString(server_info_tmp->server_id).  
  28.           c_str());  
  29.     }     
  30.     //若在TAIR_SERVER_DOWNTIME * 2内收到了对端配置服务器的心跳包,并且当前记录状态为down,则修改其为alive。这表示另外一个配置服务器已经活过来了(或许是人工重新启动的)  
  31.     else if(server_info_tmp->last_time > now && server_info_tmp->status == server_info::DOWN) {        // uphost  
  32.       server_info_tmp->status = server_info::ALIVE;  
  33.       if(i == 0)  
  34.         master_status_changed = true;  
  35.       config_server_up = true;  
  36.       log_warn("CONFIG HOST UP: %s",  
  37.           tbsys::CNetUtil::addrToString(server_info_tmp->server_id).  
  38.           c_str());  
  39.     }     
  40.   }  
  41.   // 没有发生主配置服务器切换,并且备配置服务器起来了,需要将所有group的client version 和 server version都增加一个常量(这里是10)  
  42.   if(master_status_changed == false) {  
  43.     if(config_server_up  
  44.         && master_config_server_id == util::local_server_ip::ip) {  
  45.       uint64_t slave_id = get_slave_server_id();  
  46.       group_info_map::iterator it;   
  47.       group_info_rw_locker.rdlock();  
  48.       for(it = group_info_map_data.begin();  
  49.           it != group_info_map_data.end(); ++it) {  
  50.         // for we can't get peer's version from protocol, add 10 to client version and server  
  51.         it->second->inc_version(server_up_inc_step);  
  52.         it->second->send_server_table_packet(slave_id);  
  53.         log_warn("config server up and master not changed, version changed. "  
  54.             "group name: %s, client version: %u, server version: %u",  
  55.             it->second->get_group_name(), it->second->get_client_version(), it->second->get_server_version());  
  56.       }  
  57.       group_info_rw_locker.unlock();  
  58.     }  
  59.     //若没有发生主 备切换,这里将返回,这里需要注意的。  
  60.     return;  
  61.   }  
  62.   //代码执行到这里,表示发生主备切换  
  63.   server_info *server_info_master = config_server_info_list[0];  
  64.   if(server_info_master->status == server_info::ALIVE) {  
  65.     master_config_server_id = server_info_master->server_id;  
  66.   }  
  67.   else {  
  68.     master_config_server_id = util::local_server_ip::ip;  
  69.   }  
  70.   if(master_config_server_id == util::local_server_ip::ip) {  
  71.       
  72.     //只有作为master的配置服务器才会执行下面代码,从本地配置文件中读取配置信息。这里为啥不将配置文件信息同步到备配置服务器上呢?  
  73.     const char *group_file_name =  
  74.       TBSYS_CONFIG.getString(CONFSERVER_SECTION, TAIR_GROUP_FILE, NULL);  
  75.     if(group_file_name == NULL) {  
  76.       log_error("not found %s:%s ", CONFSERVER_SECTION, TAIR_GROUP_FILE);  
  77.       return;  
  78.     }  
  79.   
  80.     uint32_t curr_version = get_file_time(group_file_name);  
  81.     load_group_file(group_file_name, curr_version, 0);  
  82.   }  
  83.   log_warn("MASTER_CONFIG changed %s",  
  84.       tbsys::CNetUtil::addrToString(master_config_server_id).  
  85.       c_str());  
  86. }  

2.4 检查数据服务器状态

函数check_server_status函数完成数据服务器状态检查工作。上代码:

[cpp]  view plain  copy
  1. void server_conf_thread::check_server_status(uint32_t loop_count)  
  2. {  
  3.   set<group_info *>change_group_info_list;  
  4.   //收集需要重新建立对照表的group,在调用该函数之前,在检查配置服务器状态的函数过程中,还有在初始化的时候,group_info_map被首次填,有可能改变group_info_map数据结构;  
  5.   //当group的配置信息发生变化后,重建对照表标志就会被置位;  
  6.   group_info_map::iterator it;   
  7.   for(it = group_info_map_data.begin(); it != group_info_map_data.end();  
  8.       ++it) {  
  9.     if(it->second->is_need_rebuild()) {  
  10.       change_group_info_list.insert(it->second);  
  11.     }     
  12.   }  
  13.   
  14.   server_info_map::iterator sit;  
  15.   for(sit = data_server_info_map.begin();  
  16.       sit != data_server_info_map.end(); ++sit) {  
  17.     //a bad one is alive again, we do not mark it ok unless some one tould us to.   
  18.     //administrator can touch group.conf to let these data serve alive again.  
  19.     uint32_t now;                // this decide how many seconds since last heart beat, we will mark this data server as a bad one.  
  20.     if(sit->second->group_info_data) {  
  21.       now =  
  22.         heartbeat_curr_time -  
  23.         sit->second->group_info_data->get_server_down_time();  
  24.     }     
  25.     else {  
  26.       now = heartbeat_curr_time - TAIR_SERVER_DOWNTIME;  
  27.     }     
  28.     if(sit->second->status != server_info::DOWN) {  
  29.     //dataserver down了,也是通过上一次收到其心跳包的时间来判断的;  
  30.       if((sit->second->last_time < now   
  31.             && (sit->second->status == server_info::ALIVE  
  32.               && !tbnet::ConnectionManager::isAlive(sit->second->server_id)))  
  33.           || sit->second->status == server_info::FORCE_DOWN) {        // downhost  
  34.         change_group_info_list.insert(sit->second->group_info_data);  
  35.         //修改dataserver的状态  
  36.         sit->second->status = server_info::DOWN;  
  37.         log_warn("HOST DOWN: %s lastTime is %u now is %u ",  
  38.             tbsys::CNetUtil::addrToString(sit->second->server_id).  
  39.             c_str(), sit->second->last_time, heartbeat_curr_time);  
  40.   
  41.         // if (need add down server config) then set downserver in group.conf  
  42.         if (sit->second->group_info_data->get_pre_load_flag() == 1)  
  43.         {     
  44.           log_error("add down host: %s lastTime is %u now is %u ",  
  45.               tbsys::CNetUtil::addrToString(sit->second->server_id).  
  46.               c_str(), sit->second->last_time, heartbeat_curr_time);  
  47.           sit->second->group_info_data->add_down_server(sit->second->server_id);  
  48.           sit->second->group_info_data->set_force_send_table();  
  49.         }  
  50.       }  
  51.     }  
  52.   }  
  53.   // only master config server can update hashtable.  
  54.   if(master_config_server_id != util::local_server_ip::ip) {  
  55.     return;  
  56.   }  
  57.   //以下代码只会在主配置服务器中执行  
  58.   uint64_t slave_server_id = get_slave_server_id();  
  59.   
  60.   group_info_rw_locker.rdlock();  
  61.   //需要检查是否数据迁移的相关状态,目前我还不明白hard check 和 check纠结各自目标是什么,这里后面慢慢分析  
  62.   if(loop_count % HARD_CHECK_MIG_COMPLETE == 0) {  
  63.     for(group_info_map::const_iterator it = group_info_map_data.begin();  
  64.         it != group_info_map_data.end(); it++) {  
  65.       it->second->hard_check_migrate_complete(slave_server_id);  
  66.     }  
  67.   }  
  68.   
  69.   for(it = group_info_map_data.begin(); it != group_info_map_data.end();  
  70.       ++it) {  
  71.     it->second->check_migrate_complete(slave_server_id);  
  72.   
  73.     // check if send server_table  
  74.     if (1 == it->second->get_send_server_table())  
  75.     {  
  76.       log_warn("group: %s need send server table", it->second->get_group_name());  
  77.       it->second->send_server_table_packet(slave_server_id);  
  78.       it->second->reset_force_send_table();  
  79.     }  
  80.   }  
  81.   
  82.   if(change_group_info_list.size() == 0U) {  
  83.     group_info_rw_locker.unlock();  
  84.     return;  
  85.   }  
  86.   //收集需要重建对照表的group, 将group添加到builder_thread线程中  
  87.   set<group_info *>::iterator cit;  
  88.   tbsys::CThreadGuard guard(&mutex_grp_need_build);  
  89.   for(cit = change_group_info_list.begin();  
  90.       cit != change_group_info_list.end(); ++cit) {  
  91.     builder_thread.group_need_build.push_back(*cit);  
  92.     (*cit)->set_table_builded();  
  93.   }  
  94.   group_info_rw_locker.unlock();  
  95. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值