研究Atlas源码,首先需要从程序的起点开始。Atlas的主函数位于
int main_cmdline(int argc, char **argv)
{ .....}
由于Atlas是基于glibc完成的开发,因此其在启动过程首先做的是验证glibc的版本:
if (chassis_frontend_init_glib()) { /* init the thread, module, ... system */
GOTO_EXIT(EXIT_FAILURE);
}
chassis_log *log = NULL;
log = chassis_log_new();
log->min_lvl = G_LOG_LEVEL_MESSAGE; /* display messages while parsing or loading plugins */
g_log_set_default_handler(chassis_log_func, log);
其中chassis_log为如下结构:
typedef struct {
GLogLevelFlags min_lvl;//日志级别
gchar *log_filename;//日志文件路径
gint log_file_fd;//日志文件描述符
gboolean use_syslog;
gboolean rotate_logs;
GString *log_ts_str;
gint log_ts_resolution; /*<< timestamp resolution (sec, ms) */
GString *last_msg;
time_t last_msg_ts;
guint last_msg_count;
} chassis_log;
由此,之后便可以通过g_log等函数实现标准化的日志输出。
随后,Atlas将进行命令行参数及配置文件的解析工作。解析出来的数据保存在
chassis_frontend_t *frontend;
其具体结构如下
typedef struct {
int print_version; //打印版本
int verbose_shutdown;//关闭输出
int daemon_mode;//后台运行
gchar *user;//用户
gchar *base_dir;//工作目录
int auto_base_dir;//是否使用默认目录
gchar *default_file;//默认配置文件
GKeyFile *keyfile;//配置文件对象
chassis_plugin *p;//插件
GOptionEntry *config_entries;//命令行配置选项
gchar *pid_file;//pid文件存放处
gchar *plugin_dir;//插件目录
gchar **plugin_names;//插件名
guint invoke_dbg_on_crash;
gint max_files_number;//最大文件数
gchar *log_level;//日志级别
gchar *log_path;//日志存放路径
int use_syslog;//是否启用syslog
char *lua_path;//lua脚本存放路径
char *lua_cpath;//
char **lua_subdirs;//lua脚本子目录
} chassis_frontend_t;
实际上,Atlas在解析命令行和配置文件的过程中是有一定的顺序的,其首先要从配置项中获取两个参数 frontend->print_version 和 frontend->default_file。
chassis_frontend_init_base_options(option_ctx, &argc, &argv, &(frontend->print_version),&(frontend->default_file),&gerr));
然后根据 frontend->default_file指定的路径加载配置文件:
frontend->keyfile = chassis_frontend_open_config_file(frontend->default_file, &gerr);
下一步,开始解析命令行和配置文件中的其他配置参数
chassis_options_t *opts = NULL;
opts = chassis_options_new();
chassis_frontend_set_chassis_options(frontend, opts);
main_entries = chassis_options_to_g_option_entries(opts);
g_option_context_add_main_entries(option_ctx, main_entries, NULL);//指定需要解析的配置命令
g_option_context_parse(option_ctx, &argc, &argv, &gerr);//解析命令行其他参数
chassis_keyfile_to_options(frontend->keyfile, "mysql-proxy", main_entries);解析配置文件中参数
至此,Atlas的配置加载工作就完成了。
配置加载完成之后,做了一下工作:
1. 指定lua脚本目录
2.检查工作线程数配置是否正确
3.更新Atlas server配置,并完成一部分初始化工作(主要是lua脚本运行需要的一些初始化工作,这一部分设计到部分lua脚本运行的问题,目前不是很懂)
network_mysqld_init(srv);
接下来,其对SIGSEGV信号设置了相应的处理函数
memset(&sigsegv_sa, 0, sizeof(sigsegv_sa));
sigsegv_sa.sa_handler = sigsegv_handler;
sigemptyset(&sigsegv_sa.sa_mask);
if (frontend->invoke_dbg_on_crash && !(RUNNING_ON_VALGRIND)) {
sigaction(SIGSEGV, &sigsegv_sa, NULL);
}
sigsegv_handler中主要做了两个工作:跟踪错误位置,抛出SIGABRT信号。
随后,Atlas开始为创建log和pid文件做准备,并根据配置文件重新设定日志级别,这部分的代码比较好理解,就不赘述了。
接下来,Atlas开始加载插件,mysql和proxy。
if (!frontend->plugin_names) {
frontend->plugin_names = g_new(char *, 3);
frontend->plugin_names[0] = g_strdup("admin");
frontend->plugin_names[1] = g_strdup("proxy");
frontend->plugin_names[2] = NULL;
}
//在这里加载插件,使用glibc的方式,关于这一部分可以参考http://www.ibm.com/developerworks/cn/linux/l-gtkplgin/
if (chassis_frontend_load_plugins(srv->modules,
frontend->plugin_dir,
frontend->plugin_names)) {
GOTO_EXIT(EXIT_FAILURE);
}
//这里的主要工作是获取插件的配置选项,并先读入部分配置
if (chassis_frontend_init_plugins(srv->modules,
option_ctx,
&argc, &argv,
frontend->keyfile,
"mysql-proxy",
srv->base_dir,
&gerr)) {
g_critical("%s: %s",
G_STRLOC,
gerr->message);
g_clear_error(&gerr);
GOTO_EXIT(EXIT_FAILURE);
}
插件加载完毕后,就可以开始解析命令行参数中之前未知的一些命令行参数:
g_option_context_parse(option_ctx, &argc, &argv, &gerr);
1. deamon模式运行
2. 修改pid文件
3. 设定最大文件限制
最后,其进入服务器程序主循环:
chassis_mainloop(srv);