skynet加载配置文件

看了skynet加载配置的地方,竟然发现好多知识点,记录下.

skynet的配置文件是通过命令行传入到进程的,这个比较明了. 仔细读了一下这块代码,发现有不少细节,而且,有些lua函数竟然很少用过,下面就这块代码分析一下.

int
main(int argc, char *argv[]) {
    const char * config_file = NULL ;
    if (argc > 1) {
        config_file = argv[1];
    } else {
        fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
            "usage: skynet configfilename\n");
        return 1;
    }

    luaS_initshr();
    skynet_globalinit();
    skynet_env_init();

    sigign();

    struct skynet_config config;

    struct lua_State *L = lua_newstate(skynet_lalloc, NULL);
    luaL_openlibs(L);   // link lua lib

    int err = luaL_loadstring(L, load_config);
    assert(err == LUA_OK);
    lua_pushstring(L, config_file);

    err = lua_pcall(L, 1, 1, 0);
    if (err) {
        fprintf(stderr,"%s\n",lua_tostring(L,-1));
        lua_close(L);
        return 1;
    }
    _init_env(L);

    config.thread =  optint("thread",8);
    config.module_path = optstring("cpath","./cservice/?.so");
    config.harbor = optint("harbor", 1);
    config.bootstrap = optstring("bootstrap","snlua bootstrap");
    config.daemon = optstring("daemon", NULL);
    config.logger = optstring("logger", NULL);
    config.logservice = optstring("logservice", "logger");

    lua_close(L);

    skynet_start(&config);
    skynet_globalexit();
    luaS_exitshr();
    return 0;
}

加载配置文件通过luaL_loadstring,lua_pushstring,lua_pcall三个lua api来完成.

luaL_loadstring(load_config)加载一段lua字符串代码,整理后如下:

local f = assert(io.open(...))
local code = assert(f:read('*a'))
local function getenv(name)
    return assert(os.getenv(name), 'os.getenv() failed: config')
end
code = string.gsub(code, '%$([%w_%d]+)', getenv)
f:close()
local result = {}
assert(load(code, '=(load)', 't', result))()
return result

我们注意到io.open的参数是...,其实是通过lua_pushstring(config_file)传递进去的.lua_pcall就是执行这段代码,并有一个返回值.

执行在上面的代码,读取配置文件的内容. string.gsub()用来查找形如'XXX',一般是系统的环境变量,例如HOME,然后用getenv()函数执行的结果来替换. 这一点有时也挺有用,比如写路径时可以直接引用系统的变量路径值.

再后面就是load,他的作用是把code字符串作为一个函数体,然后执行.这样说还是不清楚,我们看下一个例子:

b = 100
f = load('return b')
print(f())

f就相当于f = function() return b end,区别是如果b是个local变量,那么在load中则不可见.

第二个参数'=(load)'在错误消息和调试消息中,用于代码块的名字。 如果不提供此参数,它默认为字符串chunk 。 chunk 不是字符串时,则为 "=(load)" 。字符串't'用于控制代码块是文本还是二进制(即预编译代码块)。 它可以是字符串 "b" (只能是二进制代码块), "t" (只能是文本代码块), 或 "bt" (可以是二进制也可以是文本)。 默认值为 "bt"。result用来接收函数的上值. 所谓上值就是函数里的全局变量,即配置文件里的形如'thread = 8'的值,都保存在result里, 并且返回result,供下面的c环境中使用.

接下来就是将上面的result通过skynet_setenv写入到全局的lua虚拟机中.这样全局虚拟机中就有了配置文件中的键值对.skynet_start()函数有个skynet_config类型的参数,他是通过optint,optstring函数从前面说的全局lua虚拟机中获取的. 就这样skynet入口获取到了配置文件中的值.

你可能感兴趣的:(skynet加载配置文件)