Tair 是分布式、高性能、可扩展、搞可靠性的存储系统。目前,Tair已成为开源项目,代码在:http://code.taobao.org/p/tair/src/. 关于Tair的更多介绍,见http://code.taobao.org/p/tair/wiki/index/。
几个月以前,我就有计划写一个TAIR代码分析文档。由于个人精力和时间的限制,迟迟未动笔。在13年伊始,我真心觉得有必要对整个系统代码作一个深入的了解。我的突破口是从客户端开始入手。当线上遇到问题时候,希望能够快速定位问题原因,并排除之。于是乎,我正式动工了。闲话不说了。
1. 代码结构
首先,简要介绍Tair的代码结构,如下所示:
.
|-- AUTHORS
|-- COPYING
|-- ChangeLog
|-- Makefile.am
|-- NEWS
|-- README
|-- bootstrap.sh
|-- configure.ac
|-- contrib
|-- doc
|-- packages
|-- rpm
|-- scripts
|-- share
|-- src
| |-- Makefile.am
| |-- client
| |-- common
| |-- configserver
| |-- dataserver
| |-- invalserver
| |-- packets
| |-- plugin
| |-- statserver
| | |-- Makefile.am
| | |-- include
| | `-- storage
| |-- storage
| | |-- fdb
| | |-- kdb
| | |-- ldb
| | |-- mdb
| | `-- storage_manager.hpp
| `-- tools
|-- tcmalloc.m4
`-- 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函数吧,代码如下:
main(int argc, char *argv[])
{
char *config_file = parse_cmd_line(argc, argv);
if(TBSYS_CONFIG.load(config_file)) {
return EXIT_FAILURE;
}
if(!tbsys::CFileUtil::mkdirs(dir_path)) {
return EXIT_FAILURE;
}
if(tbsys::CProcess::startDaemon(sz_pid_file, sz_log_file) == 0) {
tair_cfg_svr = new tair::config_server::tair_config_server();
tair_cfg_svr->start();
delete tair_cfg_svr;
}
return EXIT_SUCCESS;
}
为了方便分析,上述代码经过精简的。可以看出,main函数在加载配置文件内容到内存,创建工作目录,将本线程设置为后台线程。接着,创建tair::config_server对象,调用start函数,configserver就在完成初始化后,就开始对外提供服务了。
2.2 configserver初始化
configserver的初始化工作主要有start中完成的。该函数首先启动线程池,该线程池用于处理各种请求;打开2个TCP端口,一个用于处理对外提供服务,另外一个用于主备configserver之间的心跳;再开一个称为my_server_conf_thread的线程。代码如下:
void tair_config_server::start()
{
if(initialize()) {
return;
}
task_queue_thread.start();
if(heartbeat_transport.listen(spec, &packet_streamer, this) == NULL) {
}
if(packet_transport.listen(spec, &packet_streamer, this) == NULL) {
}
packet_transport.start();
heartbeat_transport.start();
my_server_conf_thread.start();
task_queue_thread.wait();
my_server_conf_thread.wait();
packet_transport.wait();
heartbeat_transport.wait();
destroy();
}
task_queue_thread.start, 在没有请求请求来的情况下,就会等待;packet_transport, heartbeat_transport.start 后,将接受到的请求,放入到线程池队列中,工作线程处理请求。这里主要分析my_server_conf_thread线程。该线程的run函数做了如下工作:
1) 读取配置文件,建立保存group信息的数据结构,这些信息包括该group的数据服务器列表等;
2) 如果主配置服务器存在并且alive, 备配置服务器从主配置服务器上读取group的配置信息,而不是从配置文件中读取;
3)