skynet启动时读取配置文件

新入门skynet系列视频b站网址 https://www.bilibili.com/video/BV19d4y1678X

skynet启动时读取配置文件

行11 skynet启动时,我们启动了一个lua虚拟机,这个虚拟机是在整个skynet生命周期中存在的。我们把他叫做skynet的环境虚拟机

行13 之后启动了一个临时虚拟机,把配置信息里面的配置选项都读取出来,然后保存到skynet的环境虚拟机中。之后关闭这个临时虚拟机

void
skynet_env_init() {
	E = skynet_malloc(sizeof(*E));
	SPIN_INIT(E)
	E->L = luaL_newstate();//启动一个环境虚拟机
}

int
main(int argc, char *argv[]) {
    //...
    skynet_env_init();
    //...
    struct lua_State *L = luaL_newstate();//启动一个临时虚拟机
	luaL_openlibs(L);	// link lua lib

	int err =  luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");
	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);
    
}

我们下面看配置项都是怎么读取出来的。

行16 是 load_config表示的 字符串加载,然后编译成一个匿名函数

行18 是给这个匿名函数提供一个参数 参数就是我们skynet启动时提供的 配置文件的路径+文件名 比如 ./example/config

行20 是执行这个匿名函数 把配置项都存入一个表result中

行26 最终把这个result表中的键值对都保存到 环境虚拟机

我们先看 load_config 表示的lua代码

	local result = {} --收集配置项 即键值对
	
	local function getenv(name) return assert(os.getenv(name), [[os.getenv() failed: ]] .. name) end
	
	local sep = package.config:sub(1,1) --sep是 /
	local current_path = [[.]]..sep
	
	local function include(filename)
		local last_path = current_path
		local path, name = filename:match([[(.*]]..sep..[[)(.*)$]])--这里表示字符串的形式是 [[字符串]]
		if path then 
			if path:sub(1,1) == sep then	--绝对路径 即path是 /xxx/yyy/zzz/
				current_path = path
			else
				current_path = current_path .. path --相对路径 即path是 aaa/bbb/
			end
		else
			name = filename
		end
		local f = assert(io.open(current_path .. name)) --打开配置文件
		local code = assert(f:read [[*a]]) --读取
		code = string.gsub(code, [[%$([%w_%d]+)]], getenv)--把系统的环境变量替换成对应的值
		f:close() --注意下面的代码 每次都把键值对放到result中保存
		assert(load(code,[[@]]..filename,[[t]],result))() --把配置文件编译成匿名函数并执行 注意环境 
		current_path = last_path
	
	end

	setmetatable(result, { __index = { include = include } })
	local config_name = ...--这里就是我们启动skynet时的配置文件 比如 ./example/config
	include(config_name) --开始处理配置文件
	setmetatable(result, nil)
	
	return result --最终返回配置项的集合

行5->行23 是我们主要讨论的读取配置文件的函数 local function include(filename) --dosomething end

我们给一个配置文件的样子

include(/one/two/config1) 	--这里是绝对路径
include(three/config2) 		--这里是config2是相对于当前配置文件的位置 也就是 ./three/config2
key1 = "xxxxx"
key2 = "yyyyy"

最终读取到全局虚拟机中的函数是 _init_env(L);这个函数实际上就是把result里面的键值对遍历出来。

static void
_init_env(lua_State *L) {//L是临时的lua虚拟机
	lua_pushnil(L);  /* first key */ //此时-2的位置起始就是result表
	while (lua_next(L, -2) != 0) {//此时已经把result表中的一对键值对读取出来,
		int keyt = lua_type(L, -2);//key放在-2的位置
		if (keyt != LUA_TSTRING) {
			fprintf(stderr, "Invalid config table\n");
			exit(1);
		}
		const char * key = lua_tostring(L,-2);//key放在-2的位置
		if (lua_type(L,-1) == LUA_TBOOLEAN) {//如果value是lua的布尔类型
			int b = lua_toboolean(L,-1);
			skynet_setenv(key,b ? "true" : "false" );
		} else {
			const char * value = lua_tostring(L,-1);//value放在-1的位置
			if (value == NULL) {
				fprintf(stderr, "Invalid config table key = %s\n", key);
				exit(1);
			}
			skynet_setenv(key,value);
		}
		lua_pop(L,1);
	}
	lua_pop(L,1);
}

我们看skynet_setenv(key,value);就是最终把键值对保存到环境虚拟机

void 
skynet_setenv(const char *key, const char *value) {
	SPIN_LOCK(E)
	
	lua_State *L = E->L;
	lua_getglobal(L, key);
	assert(lua_isnil(L, -1));//注意 重复设置键值对是会报错的
	lua_pop(L,1);//下面两行设置键值对
	lua_pushstring(L,value);
	lua_setglobal(L,key);

	SPIN_UNLOCK(E)
}
posted @ 2022-12-08 15:22  程序员阿钢  阅读(202)  评论(0编辑  收藏  举报