本篇文章简单解读一下drogon的默认配置文件及其加载过程。
我们可以使用如下命令创建一个新的项目:
drogon_ctl create project xxx
默认的main.cc
文件内只是监听了一个80端口号,并在主函数的最后使用app().run();
运行起整个框架。
#include <drogon/drogon.h>
int main() {
//Set HTTP listener address and port
drogon::app().addListener("0.0.0.0",80);
//Load config file
//drogon::app().loadConfigFile("../config.json");
//Run HTTP framework,the method will block in the internal event loop
drogon::app().run();
return 0;
}
对main.cc
文件做出如下修改:
#include <drogon/drogon.h>
int main() {
drogon::app()
.loadConfigFile("../config.json")
.run();
return 0;
}
从官方的wiki文档以及生成的默认代码的注释中我们都可以得知app().loadConfigFile()
函数可以加载一个json格式的文件并进行相应的配置。同时修改后的代码也使用了链式调用,这一点并不重要,只是让代码看起来更简洁一些。
app()的调用链如下:app()
→ HttpAppFramework::instance()
→ HttpAppFrameworkImpl::instance()
。其中HttpAppFramework
类是一个抽象类,Impl是它的一个子类。最终会返回给我们一个Impl类的实例。
loadConfigFile
的实现如下:
HttpAppFramework &HttpAppFrameworkImpl::loadConfigFile(
const std::string &fileName)
{
ConfigLoader loader(fileName);//构造函数会读取文件内容并存储到成员变量中
loader.load();//重头戏
jsonConfig_ = loader.jsonValue();//框架内缓存一下文件内容,后续如getCustomConfig, run等函数会读取这里的值
return *this;
}
ConfigLoader
的构造函数:
ConfigLoader::ConfigLoader(const std::string &configFile)
{
//判断文件是否存在……
//判断是否有读取文件的权限……
configFile_ = configFile;//文件名存到成员变量中
std::ifstream infile(drogon::utils::toNativePath(configFile).c_str(),
std::ifstream::in);
if (infile)
{
//这里省略掉try-catch块
infile >> configJsonRoot_;//文件内容按照JsonValue的格式存储到成员变量中
//如果有错误,会自动输出错误位置和可能的错误原因并结束程序的运行
}
}
ConfigLoader::load()
函数内容如下:
void ConfigLoader::load()
{
// std::cout<<configJsonRoot_<<std::endl;
loadApp(configJsonRoot_["app"]);
loadSSL(configJsonRoot_["ssl"]);
loadListeners(configJsonRoot_["listeners"]);
loadDbClients(configJsonRoot_["db_clients"]);
loadRedisClients(configJsonRoot_["redis_clients"]);
}
其中的每一个函数都会从构造函数里读取完的内容取出一条子项并进行逐条分析,其中的重头戏是loadApp
:
static void loadApp(const Json::Value &app)
{
// ...
// session
auto enableSession = app.get("enable_session", false).asBool();
auto timeout = app.get("session_timeout", 0).asUInt64();
if (enableSession)
drogon::app().enableSession(timeout);
else
drogon::app().disableSession();
//...
// document root
auto documentRoot = app.get("document_root", "").asString();
if (documentRoot != "")
{
drogon::app().setDocumentRoot(documentRoot);
}
//...
}
loadApp
里读取了43条app
的子项(默认生成的config.json
文件中只有39条),调用了35个不同的配置函数完成了自动配置。
比如enable_session
以及session_timeout
两条配置项决定调用enableSession
还是disableSession
函数,以及在调用enableSession
的同时设置过期时间。
以app().setDocumentRoot()
函数为例:
HttpAppFramework &setDocumentRoot(const std::string &rootPath) override
{
rootPath_ = rootPath;
return *this;
}
它做的事情很简单,就是把传进来的参数存储到了成员变量中。
后续在调用app().run()
的时候会读取这些成员变量的值来决定具体的操作:
比如在run()→setupFileLogger()
函数中,就会读取logfileBaseName_
成员变量。
这一个成员变量是在app().setLogPath()
函数中设置的,我们当然可以直接调用这个函数来进行设置,但是在ConfigLoader::loadLogSetting()
函数中会自动帮我们调用,而ConfigLoader::loadapp()
在调用这个函数时会自动传递进来app["log"]
。
所以我们既可以直接调用app().setLogPath()
,又可以通过在配置文件中编写app.log
,来实现设置日志文件的存储位置。
源码之前,了无秘密。
而drogon源码的阅读,loadConfigFile()是一个不错的入手点。
评论