学习开源的软件,最重要的要找到入口,从入口开始学习是至关重要的,就像一团杂乱无章的线团,只要我们能够找到线头,一切都是那么的简单,而入口main函数就是这团杂乱无章的线头。废话少说,下面我们就从main函数开始学习。
main函数主要完成以下功能:
这里参数解析采用的是boost库program_options,简介请参考Boost.Program_options简述,专业版请参考Boost.Program_options。参数分成两部分,第一部分是命令行参数,第二部分是配置文件参数(如果命令行中指定了config-file)。
boost::program_options::options_description all("Allowed options");
//指定通用的配置参数
all.add(general);
//指定HTTP的配置参数
gearmand::protocol::HTTP http;
all.add(http.command_line_options());
//指定gear的配置参数
gearmand::protocol::Gear gear;
all.add(gear.command_line_options());
//指定插件的配置参数
gearmand::plugins::initialize(all);
boost::program_options::positional_options_description positional;
positional.add("provided", -1);
// 指定help参数可见的选项
boost::program_options::options_description visible("Allowed options");
visible.add(all);
//指定用户不可见的选项
boost::program_options::options_description hidden("Hidden options");
hidden.add_options()
("check-args", boost::program_options::bool_switch(&opt_check_args)->default_value(false),
"Check command line and configuration file argments and then exit.");
all.add(hidden);
//解析命令行配置参数
// Disable allow_guessing
int style= boost::program_options::command_line_style::default_style ^ boost::program_options::command_line_style::allow_guessing;
boost::program_options::parsed_options parsed= boost::program_options::command_line_parser(argc, argv)
.options(all)
.positional(positional)
.style(style)
.run();
store(parsed, vm);
notify(vm);
当指定了config-file选项时,则会解析配置文件,这里需要注意的是,当配置文件指定的参数和命令行指定的参数重复时,将会以命令行指定的为准。program_options中最先解析的参数将覆盖最后解析的,在程序中先解析命令行参数,后解析配置文件。
std::ifstream ifs(config_file.c_str());
if (ifs)
{
//读取文件内容
std::stringstream ss;
ss << ifs.rdbuf();
//按行拆分,并转换为每个配置
boost::char_separator<char> sep(" \n\r");
std::string sstr= ss.str();
boost::tokenizer<boost::char_separator<char> > tok(sstr, sep);
std::vector<std::string> args;
std::copy(tok.begin(), tok.end(), back_inserter(args));
for (std::vector<std::string>::iterator iter= args.begin();iter != args.end();++iter)
{
std::cerr << *iter << std::endl;
}
//解析配置文件内容
store(boost::program_options::command_line_parser(args).options(visible).run(), vm);
}
notify(vm);
//解析日志的级别
gearmand_verbose_t verbose= GEARMAND_VERBOSE_ERROR;
if (gearmand_verbose_check(verbose_string.c_str(), verbose) == false)
{
error::message("Invalid value for --verbose supplied");
return EXIT_FAILURE;
}
if (hashtable_buckets <= 0)
{
error::message("hashtable-buckets has to be greater than 0");
return EXIT_FAILURE;
}
if (opt_check_args)
{
return EXIT_SUCCESS;
}
//指定帮助信息时,输出相应的帮助信息
if (vm.count("help"))
{
std::cout << visible << std::endl;
return EXIT_SUCCESS;
}
//输出version信息
if (vm.count("version"))
{
std::cout << std::endl << "gearmand " << gearmand_version() << " - " << gearmand_bugreport() << std::endl;
return EXIT_SUCCESS;
}
//设置能够打开的最大文件描述符个数
if (fds > 0 and _set_fdlimit(fds))
{
return EXIT_FAILURE;
}
#当为root时,切换到指定的用户名
if (not user.empty() and _switch_user(user.c_str()))
{
return EXIT_FAILURE;
}
//启动daemon,后面还需要关闭父进程打开的文件描述符才能完全的完成daemon化
if (opt_daemon)
{
util::daemonize(false, true);
}
//设置相应的信号处理句柄
if (_set_signals(opt_coredump))
{
return EXIT_FAILURE;
}
//记录进程启动的pid
util::Pidfile _pid_file(pid_file);
if (_pid_file.create() == false and pid_file.compare(GEARMAND_PID))
{
error::perror(_pid_file.error_message().c_str());
return EXIT_FAILURE;
}
//初始化日志的配置
gearmand::gearmand_log_info_st log_info(log_file, opt_syslog);
if (log_info.initialized() == false)
{
return EXIT_FAILURE;
}
//设置开启的线程数
if (threads == 0)
{
uint32_t number_of_threads= libtest::number_of_cpus();
if (number_of_threads > 4)
{
threads= number_of_threads;
}
}
//初始化gearman的配置
gearmand_config_st *gearmand_config= gearmand_config_create();
if (gearmand_config == NULL)
{
return EXIT_FAILURE;
}
//初始化socket选项
gearmand_config_sockopt_keepalive(gearmand_config, opt_keepalive);
//初始化gearman配置
gearmand_st *_gearmand= gearmand_create(gearmand_config,
host.empty() ? NULL : host.c_str(),threads,backlog,
job_handle_prefix.empty()?NULL:job_handle_prefix.c_str(),
static_cast<uint8_t>(worker_wakeup),_log, &log_info,
hashtable_buckets);
if (_gearmand == NULL)
{
error::message("Could not create gearmand library instance.");
return EXIT_FAILURE;
}
gearmand_config_free(gearmand_config);
//初始化插件
assert(queue_type.size());
if (queue_type.empty() == false)
{
gearmand_error_t rc;
if ((rc= gearmand::queue::initialize(_gearmand, queue_type.c_str())) != GEARMAND_SUCCESS)
{
error::message("Error while initializing the queue", queue_type.c_str());
gearmand_free(_gearmand);
return EXIT_FAILURE;
}
}
//初始化gearman
if (gear.start(_gearmand) != GEARMAND_SUCCESS)
{
error::message("Error while enabling Gear protocol module");
gearmand_free(_gearmand);
return EXIT_FAILURE;
}
//初始化http
if (protocol.compare("http") == 0)
{
if (http.start(_gearmand) != GEARMAND_SUCCESS)
{
error::message("Error while enabling protocol module", protocol.c_str());
gearmand_free(_gearmand);
return EXIT_FAILURE;
}
}
else if (protocol.empty() == false)
{
error::message("Unknown protocol module", protocol.c_str());
gearmand_free(_gearmand);
return EXIT_FAILURE;
}
//关闭父进程打开的文件描述符,完成最后的daemon
if (opt_daemon)
{
if (util::daemon_is_ready(true) == false)
{
return EXIT_FAILURE;
}
}
//开启事件处理线程,正式开始提供服务(死循环)
gearmand_error_t ret= gearmand_run(_gearmand);
//释放gearman的资源
gearmand_free(_gearmand);
_gearmand= NULL;
至此,main函数我们已经学习完毕了,后面我们就跟深入的学习他的插件以及具体的run里面的内容。下面我们先学习插件,了解你其插件的工作模式。